Commit 2581b6e7c2c for php

commit 2581b6e7c2cbdbe70fe934a910910409657e347b
Merge: 3fb2c088b77 1e24c518786
Author: Gina Peter Banyard <girgias@php.net>
Date:   Sun Nov 8 07:44:21 2026 +0000

    Merge branch 'PHP-8.5' into PHP-8.6

    * PHP-8.5:
      Zend: fix object_init_with_constructor() with positional args in named_params
      zend_test: add additional tests with zend_object_init_with_constructor()
      zend_test: add missing opcache enable INI setting

diff --cc Zend/zend_API.c
index 6ca5430d392,d196ff2061a..e43b3abf9ae
--- a/Zend/zend_API.c
+++ b/Zend/zend_API.c
@@@ -1950,19 -1904,21 +1950,21 @@@ ZEND_API zend_result object_init_with_c
  		 * However, if there are named arguments (and it is not empty),
  		 * an Error must be thrown to be consistent with new ClassName() */
  		if (UNEXPECTED(named_params != NULL && zend_hash_num_elements(named_params) != 0)) {
- 			/* Throw standard Error */
- 			zend_string *arg_name = NULL;
- 			zend_hash_get_current_key(named_params, &arg_name, /* num_index */ NULL);
- 			ZEND_ASSERT(arg_name != NULL);
- 			zend_throw_error(NULL, "Unknown named parameter $%pS", arg_name);
- 			/* Do not call destructor, free object, and set arg to IS_UNDEF */
- 			zend_object_store_ctor_failed(obj);
- 			zval_ptr_dtor(arg);
- 			ZVAL_UNDEF(arg);
- 			return FAILURE;
- 		} else {
- 			return SUCCESS;
+ 			/* As named_arguments may contain positional args we need to check there is actually a string key */
+ 			ZEND_HASH_FOREACH_STR_KEY(named_params, zend_string *arg_name) {
+ 				if (arg_name == NULL) {
+ 					continue;
+ 				}
+ 				/* Throw standard Error */
 -				zend_throw_error(NULL, "Unknown named parameter $%s", ZSTR_VAL(arg_name));
++				zend_throw_error(NULL, "Unknown named parameter $%pS", arg_name);
+ 				/* Do not call destructor, free object, and set arg to IS_UNDEF */
+ 				zend_object_store_ctor_failed(obj);
+ 				zval_ptr_dtor(arg);
+ 				ZVAL_UNDEF(arg);
+ 				return FAILURE;
+ 			} ZEND_HASH_FOREACH_END();
  		}
+ 		return SUCCESS;
  	}
  	/* A constructor should not return a value, however if an exception is thrown
  	 * zend_call_known_function() will set the retval to IS_UNDEF */
diff --cc ext/zend_test/test_arginfo.h
index c2509001e45,40c38d2e65c..2b81903ddeb
Binary files differ
diff --cc ext/zend_test/test_decl.h
index 291f7ab87af,00000000000..0dbb640bb1d
mode 100644,000000..100644
Binary files differ
diff --cc ext/zend_test/test_legacy_arginfo.h
index d6e1f758e20,00000000000..51f0a71d5b6
mode 100644,000000..100644
Binary files differ
diff --cc ext/zend_test/tests/zend_object_init_with_constructor_array.phpt
index 00000000000,0178114abcb..079eb76e2fb
mode 000000,100644..100644
--- a/ext/zend_test/tests/zend_object_init_with_constructor_array.phpt
+++ b/ext/zend_test/tests/zend_object_init_with_constructor_array.phpt
@@@ -1,0 -1,152 +1,150 @@@
+ --TEST--
+ Zend: Test object_init_with_constructor() API
+ --EXTENSIONS--
+ zend_test
+ --FILE--
+ <?php
+
+ class TestUserWithConstructorArgs {
+     public function __construct(int $int_param, string $string_param) {
 -        return new stdClass();
+     }
+     public function __destruct() {
+         echo 'Destructor for ', __CLASS__, PHP_EOL;
+     }
+ }
+
+ class TestUserWithConstructorNoParams {
+     public function __construct() {
 -        return new stdClass();
+     }
+     public function __destruct() {
+         echo 'Destructor for ', __CLASS__, PHP_EOL;
+     }
+ }
+
+ class TestUserWithoutConstructor {
+     public function __destruct() {
+         echo 'Destructor for ', __CLASS__, PHP_EOL;
+     }
+ }
+
+ $args = [5, 'str'];
+ $argsNamed = [5, 'string_param' => 'str'];
+ $argsNamedExtraUnknown = [5, 'string_param' => 'str', 'unknown' => 'unknown'];
+
+ echo "Testing param passing\n";
+ echo "Test TestUserWithConstructorArgs:\n";
+ $o = zend_object_init_with_constructor_array("TestUserWithConstructorArgs", $args);
+ var_dump($o);
+ unset($o);
+
+ $o = zend_object_init_with_constructor_array("TestUserWithConstructorArgs", $argsNamed);
+ var_dump($o);
+ unset($o);
+
+ try {
+ 	$o = zend_object_init_with_constructor_array("TestUserWithConstructorArgs", $argsNamedExtraUnknown);
+ 	var_dump($o);
+ 	unset($o);
+ } catch (\Throwable $e) {
+     echo $e::class, ': ', $e->getMessage(), PHP_EOL;
+ }
+
+ echo "Test TestUserWithConstructorNoParams:\n";
+ try {
+     $o = zend_object_init_with_constructor_array("TestUserWithConstructorNoParams", $args);
+     var_dump($o);
+     unset($o);
+ } catch (\Throwable $e) {
+     echo $e::class, ': ', $e->getMessage(), PHP_EOL;
+ }
+
+ try {
+ 	$o = zend_object_init_with_constructor_array("TestUserWithConstructorNoParams", $argsNamed);
+ 	var_dump($o);
+ 	unset($o);
+ } catch (\Throwable $e) {
+     echo $e::class, ': ', $e->getMessage(), PHP_EOL;
+ }
+
+ try {
+ 	$o = zend_object_init_with_constructor_array("TestUserWithConstructorNoParams", $argsNamedExtraUnknown);
+ 	var_dump($o);
+ 	unset($o);
+ } catch (\Throwable $e) {
+     echo $e::class, ': ', $e->getMessage(), PHP_EOL;
+ }
+
+ echo "Test TestUserWithoutConstructor:\n";
+ $o = zend_object_init_with_constructor_array("TestUserWithoutConstructor", $args);
+ var_dump($o);
+ unset($o);
+
+ try {
+     $o = zend_object_init_with_constructor_array("TestUserWithoutConstructor", $argsNamed);
+     var_dump($o);
+     unset($o);
+ } catch (\Throwable $e) {
+     echo $e::class, ': ', $e->getMessage(), PHP_EOL;
+ }
+
+ try {
+     $o = zend_object_init_with_constructor_array("TestUserWithoutConstructor", $argsNamedExtraUnknown);
+     var_dump($o);
+     unset($o);
+ } catch (\Throwable $e) {
+     echo $e::class, ': ', $e->getMessage(), PHP_EOL;
+ }
+
+ echo "Test _ZendTestMagicCall (internal class no ctor):\n";
+ try {
+     $o = zend_object_init_with_constructor_array("_ZendTestMagicCall", $args);
+     var_dump($o);
+     unset($o);
+ } catch (\Throwable $e) {
+     echo $e::class, ': ', $e->getMessage(), PHP_EOL;
+ }
+
+ try {
+     $o = zend_object_init_with_constructor_array("_ZendTestMagicCall", $argsNamed);
+     var_dump($o);
+     unset($o);
+ } catch (\Throwable $e) {
+     echo $e::class, ': ', $e->getMessage(), PHP_EOL;
+ }
+
+ try {
+     $o = zend_object_init_with_constructor_array("_ZendTestMagicCall", $argsNamedExtraUnknown);
+     var_dump($o);
+     unset($o);
+ } catch (\Throwable $e) {
+     echo $e::class, ': ', $e->getMessage(), PHP_EOL;
+ }
+
+ ?>
+ --EXPECT--
+ Testing param passing
+ Test TestUserWithConstructorArgs:
+ object(TestUserWithConstructorArgs)#1 (0) {
+ }
+ Destructor for TestUserWithConstructorArgs
+ object(TestUserWithConstructorArgs)#1 (0) {
+ }
+ Destructor for TestUserWithConstructorArgs
+ Error: Unknown named parameter $unknown
+ Test TestUserWithConstructorNoParams:
+ object(TestUserWithConstructorNoParams)#1 (0) {
+ }
+ Destructor for TestUserWithConstructorNoParams
+ Error: Unknown named parameter $string_param
+ Error: Unknown named parameter $string_param
+ Test TestUserWithoutConstructor:
+ object(TestUserWithoutConstructor)#3 (0) {
+ }
+ Destructor for TestUserWithoutConstructor
+ Error: Unknown named parameter $string_param
+ Error: Unknown named parameter $string_param
+ Test _ZendTestMagicCall (internal class no ctor):
+ object(_ZendTestMagicCall)#2 (0) {
+ }
+ Error: Unknown named parameter $string_param
+ Error: Unknown named parameter $string_param