From ff7ef3c858fc54f8b62c1d274ce638523e202f09 Mon Sep 17 00:00:00 2001 From: NickSdot <32384907+NickSdot@users.noreply.github.com> Date: Tue, 11 Aug 2026 03:24:29 +0700 Subject: [PATCH 1/6] ext/standard: improved tests (#23184) --- Zend/tests/named_params/internal_variadics.phpt | 16 ++++++++-------- .../tests/array/array_intersect_empty.phpt | 4 ++-- .../array/array_intersect_side_effects.phpt | 4 ++-- .../tests/array/array_intersect_values.phpt | 12 ++++++------ 4 files changed, 18 insertions(+), 18 deletions(-) diff --git a/Zend/tests/named_params/internal_variadics.phpt b/Zend/tests/named_params/internal_variadics.phpt index 8ec11ac2c34a..ac5b629ccaae 100644 --- a/Zend/tests/named_params/internal_variadics.phpt +++ b/Zend/tests/named_params/internal_variadics.phpt @@ -6,13 +6,13 @@ Named params on internal functions: Variadic functions that don't support extra try { array_merge([1, 2], a: [3, 4]); } catch (ArgumentCountError $e) { - echo $e->getMessage(), "\n"; + echo $e::class, ': ', $e->getMessage(), "\n"; } try { array_diff_key([1, 2], [3, 4], a: [5, 6]); } catch (ArgumentCountError $e) { - echo $e->getMessage(), "\n"; + echo $e::class, ': ', $e->getMessage(), "\n"; } var_dump(array_intersect(array: [1, 2]) === [1, 2]); @@ -20,19 +20,19 @@ var_dump(array_intersect(array: [1, 2]) === [1, 2]); try { array_intersect([1, 2], arrays: [2]); } catch (ArgumentCountError $e) { - echo $e->getMessage(), "\n"; + echo $e::class, ': ', $e->getMessage(), "\n"; } try { $array = [1, 2]; array_push($array, ...['values' => 3]); } catch (ArgumentCountError $e) { - echo $e->getMessage(), "\n"; + echo $e::class, ': ', $e->getMessage(), "\n"; } ?> --EXPECT-- -Internal function array_merge() does not accept named variadic arguments -Internal function array_diff_key() does not accept named variadic arguments +ArgumentCountError: Internal function array_merge() does not accept named variadic arguments +ArgumentCountError: Internal function array_diff_key() does not accept named variadic arguments bool(true) -Internal function array_intersect() does not accept named variadic arguments -Internal function array_push() does not accept named variadic arguments +ArgumentCountError: Internal function array_intersect() does not accept named variadic arguments +ArgumentCountError: Internal function array_push() does not accept named variadic arguments diff --git a/ext/standard/tests/array/array_intersect_empty.phpt b/ext/standard/tests/array/array_intersect_empty.phpt index 1bf527a42694..ab5ae218b7c4 100644 --- a/ext/standard/tests/array/array_intersect_empty.phpt +++ b/ext/standard/tests/array/array_intersect_empty.phpt @@ -36,7 +36,7 @@ var_dump(array_keys($result)); try { array_intersect([], [], new stdClass()); } catch (TypeError $e) { - echo $e->getMessage(), "\n"; + echo $e::class, ': ', $e->getMessage(), "\n"; } ?> --EXPECT-- @@ -52,4 +52,4 @@ array(1) { [0]=> int(10) } -array_intersect(): Argument #3 must be of type array, stdClass given +TypeError: array_intersect(): Argument #3 must be of type array, stdClass given diff --git a/ext/standard/tests/array/array_intersect_side_effects.phpt b/ext/standard/tests/array/array_intersect_side_effects.phpt index 00cacb9b4a39..6c2a2451fdc6 100644 --- a/ext/standard/tests/array/array_intersect_side_effects.phpt +++ b/ext/standard/tests/array/array_intersect_side_effects.phpt @@ -47,7 +47,7 @@ set_error_handler(static function (int $code, string $message): bool { try { array_intersect([[1], [2]], ['Array'], new stdClass()); } catch (TypeError $e) { - echo $e->getMessage(), "\n"; + echo $e::class, ': ', $e->getMessage(), "\n"; } restore_error_handler(); @@ -67,4 +67,4 @@ array(2) { [1]=> string(4) "keep" } -array_intersect(): Argument #3 must be of type array, stdClass given +TypeError: array_intersect(): Argument #3 must be of type array, stdClass given diff --git a/ext/standard/tests/array/array_intersect_values.phpt b/ext/standard/tests/array/array_intersect_values.phpt index caac782346b0..53234e103cbb 100644 --- a/ext/standard/tests/array/array_intersect_values.phpt +++ b/ext/standard/tests/array/array_intersect_values.phpt @@ -67,13 +67,13 @@ class ThrowingStringableValue { try { array_intersect(['value'], [new ThrowingStringableValue(), 'value']); } catch (RuntimeException $e) { - echo $e->getMessage(), "\n"; + echo $e::class, ': ', $e->getMessage(), "\n"; } try { array_intersect([''], [new stdClass()]); } catch (Error $e) { - echo $e->getMessage(), "\n"; + echo $e::class, ': ', $e->getMessage(), "\n"; } set_error_handler(static function (int $code, string $message): never { @@ -82,7 +82,7 @@ set_error_handler(static function (int $code, string $message): never { try { array_intersect([[1]], [[2]]); } catch (ErrorException $e) { - echo $e->getMessage(), "\n"; + echo $e::class, ': ', $e->getMessage(), "\n"; } finally { restore_error_handler(); } @@ -98,6 +98,6 @@ bool(true) bool(true) null,false,true,zero-float,float,resource,object bool(true) -conversion failed -Object of class stdClass could not be converted to string -Array to string conversion +RuntimeException: conversion failed +Error: Object of class stdClass could not be converted to string +ErrorException: Array to string conversion From facf3f99cf66bba67019feb18c0f0336175a91ec Mon Sep 17 00:00:00 2001 From: Ilija Tovilo Date: Mon, 10 Aug 2026 23:11:33 +0200 Subject: [PATCH 2/6] Fix test file conflict touch_variation5-win32.phpt and touch_variation6-win32.phpt write to the same file, causing a race. --- .../tests/file/touch_variation6-win32.phpt | 132 +++++++++--------- 1 file changed, 66 insertions(+), 66 deletions(-) diff --git a/ext/standard/tests/file/touch_variation6-win32.phpt b/ext/standard/tests/file/touch_variation6-win32.phpt index d1a697c4abb4..85e43868ce05 100644 --- a/ext/standard/tests/file/touch_variation6-win32.phpt +++ b/ext/standard/tests/file/touch_variation6-win32.phpt @@ -10,7 +10,7 @@ if (substr(PHP_OS, 0, 3) != 'WIN') { ?> --FILE-- Date: Mon, 10 Aug 2026 23:04:41 +0100 Subject: [PATCH 3/6] mysqli: Deprecate mysqli_get_charset() (#23206) --- ext/mysqli/mysqli.stub.php | 2 ++ ext/mysqli/mysqli_arginfo.h | 22 ++++++++++++++++--- .../tests/mysqli_change_user_set_names.phpt | 6 +++-- ext/mysqli/tests/mysqli_fetch_field.phpt | 4 +--- ext/mysqli/tests/mysqli_fetch_field_oo.phpt | 4 +--- ext/mysqli/tests/mysqli_fetch_fields.phpt | 4 +--- ext/mysqli/tests/mysqli_field_seek.phpt | 7 +++--- ext/mysqli/tests/mysqli_get_charset.phpt | 11 +++++++++- .../mysqli_stmt_execute_stored_proc.phpt | 5 +++-- ..._stmt_get_result_metadata_fetch_field.phpt | 7 +++--- 10 files changed, 47 insertions(+), 25 deletions(-) diff --git a/ext/mysqli/mysqli.stub.php b/ext/mysqli/mysqli.stub.php index cc6b8c57404a..c995cf564a57 100644 --- a/ext/mysqli/mysqli.stub.php +++ b/ext/mysqli/mysqli.stub.php @@ -797,6 +797,7 @@ public function debug(string $options): true {} * @tentative-return-type * @alias mysqli_get_charset */ + #[\Deprecated(since: '8.6', message: 'did you mean mysqli_character_set_name()?')] public function get_charset(): ?object {} /** @@ -1463,6 +1464,7 @@ function mysqli_get_connection_stats(mysqli $mysql): array {} function mysqli_get_client_stats(): array {} /** @refcount 1 */ +#[\Deprecated(since: '8.6', message: 'did you mean mysqli_character_set_name()?')] function mysqli_get_charset(mysqli $mysql): ?object {} /** @refcount 1 */ diff --git a/ext/mysqli/mysqli_arginfo.h b/ext/mysqli/mysqli_arginfo.h index 0121f36f3cfd..1fd84063441e 100644 --- a/ext/mysqli/mysqli_arginfo.h +++ b/ext/mysqli/mysqli_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit mysqli.stub.php instead. - * Stub hash: dc804bc50cd0a0e14dafc0e03564d5699d641db0 */ + * Stub hash: d31c6ff508415337f4536e8e476168882e769158 */ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_mysqli_affected_rows, 0, 1, MAY_BE_LONG|MAY_BE_STRING) ZEND_ARG_OBJ_INFO(0, mysql, mysqli, 0) @@ -862,7 +862,7 @@ static const zend_function_entry ext_functions[] = { ZEND_FE(mysqli_free_result, arginfo_mysqli_free_result) ZEND_FE(mysqli_get_connection_stats, arginfo_mysqli_get_connection_stats) ZEND_FE(mysqli_get_client_stats, arginfo_mysqli_get_client_stats) - ZEND_FE(mysqli_get_charset, arginfo_mysqli_get_charset) + ZEND_RAW_FENTRY("mysqli_get_charset", zif_mysqli_get_charset, arginfo_mysqli_get_charset, ZEND_ACC_DEPRECATED, NULL, NULL) ZEND_FE(mysqli_get_client_info, arginfo_mysqli_get_client_info) ZEND_FE(mysqli_get_client_version, arginfo_mysqli_get_client_version) ZEND_FE(mysqli_get_links_stats, arginfo_mysqli_get_links_stats) @@ -948,7 +948,7 @@ static const zend_function_entry class_mysqli_methods[] = { ZEND_RAW_FENTRY("connect", zif_mysqli_connect, arginfo_class_mysqli_connect, ZEND_ACC_PUBLIC, NULL, NULL) ZEND_RAW_FENTRY("dump_debug_info", zif_mysqli_dump_debug_info, arginfo_class_mysqli_dump_debug_info, ZEND_ACC_PUBLIC, NULL, NULL) ZEND_RAW_FENTRY("debug", zif_mysqli_debug, arginfo_class_mysqli_debug, ZEND_ACC_PUBLIC, NULL, NULL) - ZEND_RAW_FENTRY("get_charset", zif_mysqli_get_charset, arginfo_class_mysqli_get_charset, ZEND_ACC_PUBLIC, NULL, NULL) + ZEND_RAW_FENTRY("get_charset", zif_mysqli_get_charset, arginfo_class_mysqli_get_charset, ZEND_ACC_PUBLIC|ZEND_ACC_DEPRECATED, NULL, NULL) ZEND_RAW_FENTRY("execute_query", zif_mysqli_execute_query, arginfo_class_mysqli_execute_query, ZEND_ACC_PUBLIC, NULL, NULL) ZEND_RAW_FENTRY("get_client_info", zif_mysqli_get_client_info, arginfo_class_mysqli_get_client_info, ZEND_ACC_PUBLIC|ZEND_ACC_DEPRECATED, NULL, NULL) ZEND_RAW_FENTRY("get_connection_stats", zif_mysqli_get_connection_stats, arginfo_class_mysqli_get_connection_stats, ZEND_ACC_PUBLIC, NULL, NULL) @@ -1167,6 +1167,14 @@ static void register_mysqli_symbols(int module_number) ZVAL_STR(&attribute_Deprecated_func_mysqli_execute_0->args[1].value, attribute_Deprecated_func_mysqli_execute_0_arg1_str); attribute_Deprecated_func_mysqli_execute_0->args[1].name = ZSTR_KNOWN(ZEND_STR_MESSAGE); + zend_attribute *attribute_Deprecated_func_mysqli_get_charset_0 = zend_add_function_attribute(zend_hash_str_find_ptr(CG(function_table), "mysqli_get_charset", sizeof("mysqli_get_charset") - 1), ZSTR_KNOWN(ZEND_STR_DEPRECATED_CAPITALIZED), 2); + zend_string *attribute_Deprecated_func_mysqli_get_charset_0_arg0_str = zend_string_init("8.6", strlen("8.6"), 1); + ZVAL_STR(&attribute_Deprecated_func_mysqli_get_charset_0->args[0].value, attribute_Deprecated_func_mysqli_get_charset_0_arg0_str); + attribute_Deprecated_func_mysqli_get_charset_0->args[0].name = ZSTR_KNOWN(ZEND_STR_SINCE); + zend_string *attribute_Deprecated_func_mysqli_get_charset_0_arg1_str = zend_string_init("did you mean mysqli_character_set_name()?", strlen("did you mean mysqli_character_set_name()?"), 1); + ZVAL_STR(&attribute_Deprecated_func_mysqli_get_charset_0->args[1].value, attribute_Deprecated_func_mysqli_get_charset_0_arg1_str); + attribute_Deprecated_func_mysqli_get_charset_0->args[1].name = ZSTR_KNOWN(ZEND_STR_MESSAGE); + zend_attribute *attribute_Deprecated_func_mysqli_kill_0 = zend_add_function_attribute(zend_hash_str_find_ptr(CG(function_table), "mysqli_kill", sizeof("mysqli_kill") - 1), ZSTR_KNOWN(ZEND_STR_DEPRECATED_CAPITALIZED), 2); ZVAL_STR(&attribute_Deprecated_func_mysqli_kill_0->args[0].value, ZSTR_KNOWN(ZEND_STR_8_DOT_4)); attribute_Deprecated_func_mysqli_kill_0->args[0].name = ZSTR_KNOWN(ZEND_STR_SINCE); @@ -1459,6 +1467,14 @@ static zend_class_entry *register_class_mysqli(void) zend_add_parameter_attribute(zend_hash_str_find_ptr(&class_entry->function_table, "connect", sizeof("connect") - 1), 2, ZSTR_KNOWN(ZEND_STR_SENSITIVEPARAMETER), 0); + zend_attribute *attribute_Deprecated_func_get_charset_0 = zend_add_function_attribute(zend_hash_str_find_ptr(&class_entry->function_table, "get_charset", sizeof("get_charset") - 1), ZSTR_KNOWN(ZEND_STR_DEPRECATED_CAPITALIZED), 2); + zend_string *attribute_Deprecated_func_get_charset_0_arg0_str = zend_string_init("8.6", strlen("8.6"), 1); + ZVAL_STR(&attribute_Deprecated_func_get_charset_0->args[0].value, attribute_Deprecated_func_get_charset_0_arg0_str); + attribute_Deprecated_func_get_charset_0->args[0].name = ZSTR_KNOWN(ZEND_STR_SINCE); + zend_string *attribute_Deprecated_func_get_charset_0_arg1_str = zend_string_init("did you mean mysqli_character_set_name()?", strlen("did you mean mysqli_character_set_name()?"), 1); + ZVAL_STR(&attribute_Deprecated_func_get_charset_0->args[1].value, attribute_Deprecated_func_get_charset_0_arg1_str); + attribute_Deprecated_func_get_charset_0->args[1].name = ZSTR_KNOWN(ZEND_STR_MESSAGE); + zend_attribute *attribute_Deprecated_func_get_client_info_0 = zend_add_function_attribute(zend_hash_str_find_ptr(&class_entry->function_table, "get_client_info", sizeof("get_client_info") - 1), ZSTR_KNOWN(ZEND_STR_DEPRECATED_CAPITALIZED), 2); ZVAL_STR(&attribute_Deprecated_func_get_client_info_0->args[0].value, ZSTR_KNOWN(ZEND_STR_8_DOT_1)); attribute_Deprecated_func_get_client_info_0->args[0].name = ZSTR_KNOWN(ZEND_STR_SINCE); diff --git a/ext/mysqli/tests/mysqli_change_user_set_names.phpt b/ext/mysqli/tests/mysqli_change_user_set_names.phpt index bcf957942f57..978150c3cbc6 100644 --- a/ext/mysqli/tests/mysqli_change_user_set_names.phpt +++ b/ext/mysqli/tests/mysqli_change_user_set_names.phpt @@ -125,11 +125,12 @@ require_once 'skipifconnectfailure.inc'; if (!is_object($charset = mysqli_get_charset($link))) printf("[013] Expecting object/std_class, got %s/%s\n", gettype($charset), $charset); - if ($charset->charset != $defaults['charset_connection']) + if ($charset->charset != $defaults['charset_connection'] || $link->character_set_name() != $defaults['charset_connection']) printf("[014] Expecting connection charset to be %s got %s\n", $defaults['charset_connection'], $charset->charset); + // Remove the following test when removing mysqli_get_charset() in PHP 9.0.0 if ($charset->collation != $defaults['collation_connection']) printf("[015] Expecting collation to be %s got %s\n", $defaults['collation_connection'], @@ -138,5 +139,6 @@ require_once 'skipifconnectfailure.inc'; mysqli_close($link); print "done!"; ?> ---EXPECT-- +--EXPECTF-- +Deprecated: Function mysqli_get_charset() is deprecated since 8.6, did you mean mysqli_character_set_name()? in %s on line %d done! diff --git a/ext/mysqli/tests/mysqli_fetch_field.phpt b/ext/mysqli/tests/mysqli_fetch_field.phpt index e65de2308ab4..7519e572bb55 100644 --- a/ext/mysqli/tests/mysqli_fetch_field.phpt +++ b/ext/mysqli/tests/mysqli_fetch_field.phpt @@ -15,9 +15,7 @@ require_once 'skipifconnectfailure.inc'; // Make sure that client, connection and result charsets are all the // same. Not sure whether this is strictly necessary. if (!mysqli_set_charset($link, 'utf8')) - printf("[%d] %s\n", mysqli_errno($link), mysqli_errno($link)); - - $charsetInfo = mysqli_get_charset($link); + printf("[%d] %s\n", mysqli_errno($link), mysqli_error($link)); if (!$res = mysqli_query($link, "SELECT id AS ID, label FROM test AS TEST ORDER BY id LIMIT 1")) { printf("[003] [%d] %s\n", mysqli_errno($link), mysqli_error($link)); diff --git a/ext/mysqli/tests/mysqli_fetch_field_oo.phpt b/ext/mysqli/tests/mysqli_fetch_field_oo.phpt index 0f2a6c55841e..ad8f0439568d 100644 --- a/ext/mysqli/tests/mysqli_fetch_field_oo.phpt +++ b/ext/mysqli/tests/mysqli_fetch_field_oo.phpt @@ -15,9 +15,7 @@ require_once 'skipifconnectfailure.inc'; // Make sure that client, connection and result charsets are all the // same. Not sure whether this is strictly necessary. if (!$mysqli->set_charset('utf8')) - printf("[%d] %s\n", $mysqli->errno, $mysqli->errno); - - $charsetInfo = $mysqli->get_charset(); + printf("[%d] %s\n", $mysqli->errno, $mysqli->error); if (!$res = $mysqli->query("SELECT id AS ID, label FROM test AS TEST ORDER BY id LIMIT 1")) { printf("[004] [%d] %s\n", $mysqli->errno, $mysqli->error); diff --git a/ext/mysqli/tests/mysqli_fetch_fields.phpt b/ext/mysqli/tests/mysqli_fetch_fields.phpt index 2257c4d9d060..18e2fc7fe9af 100644 --- a/ext/mysqli/tests/mysqli_fetch_fields.phpt +++ b/ext/mysqli/tests/mysqli_fetch_fields.phpt @@ -15,9 +15,7 @@ require_once 'skipifconnectfailure.inc'; // Make sure that client, connection and result charsets are all the // same. Not sure whether this is strictly necessary. if (!mysqli_set_charset($link, 'utf8')) - printf("[%d] %s\n", mysqli_errno($link), mysqli_errno($link)); - - $charsetInfo = mysqli_get_charset($link); + printf("[%d] %s\n", mysqli_errno($link), mysqli_error($link)); if (!$res = mysqli_query($link, "SELECT id AS ID, label FROM test AS TEST ORDER BY id LIMIT 1")) { printf("[003] [%d] %s\n", mysqli_errno($link), mysqli_error($link)); diff --git a/ext/mysqli/tests/mysqli_field_seek.phpt b/ext/mysqli/tests/mysqli_field_seek.phpt index af52bf6c97fb..f9fe55aaf76e 100644 --- a/ext/mysqli/tests/mysqli_field_seek.phpt +++ b/ext/mysqli/tests/mysqli_field_seek.phpt @@ -61,8 +61,6 @@ require_once 'skipifconnectfailure.inc'; if (!mysqli_set_charset($link, 'utf8')) printf("[%d] %s\n", mysqli_errno($link), mysqli_errno($link)); - $charsetInfo = mysqli_get_charset($link); - if (!$res = mysqli_query($link, "SELECT id, label FROM test ORDER BY id LIMIT 1", MYSQLI_USE_RESULT)) { printf("[003] [%d] %s\n", mysqli_errno($link), mysqli_error($link)); } @@ -80,9 +78,10 @@ require_once 'skipifconnectfailure.inc'; $field = mysqli_fetch_field($res); var_dump($field); /* label column, result set charset */ - if ($field->length != $charsetInfo->max_length) { + $charMaxLength = 3; + if ($field->length != $charMaxLength) { printf("[005] Expecting length %d got %d\n", - $charsetInfo->max_length, $field->max_length); + $charMaxLength, $field->max_length); } var_dump(mysqli_field_tell($res)); diff --git a/ext/mysqli/tests/mysqli_get_charset.phpt b/ext/mysqli/tests/mysqli_get_charset.phpt index 2752084372a9..4753823227d8 100644 --- a/ext/mysqli/tests/mysqli_get_charset.phpt +++ b/ext/mysqli/tests/mysqli_get_charset.phpt @@ -72,6 +72,10 @@ require_once 'skipifconnectfailure.inc'; !is_int($charset->state)) printf("[022] Expecting int/any, got %s/%s\n", gettype($charset->state), $charset->state); + $charsetOO = $link->get_charset(); + if ($charsetOO != $charset) + printf("[023] Expecting object/%s, got %s/%s\n", gettype($charset), gettype($charsetOO), $charsetOO); + mysqli_close($link); try { @@ -82,6 +86,11 @@ require_once 'skipifconnectfailure.inc'; print "done!"; ?> ---EXPECT-- +--EXPECTF-- +Deprecated: Function mysqli_get_charset() is deprecated since 8.6, did you mean mysqli_character_set_name()? in %s on line %d + +Deprecated: Method mysqli::get_charset() is deprecated since 8.6, did you mean mysqli_character_set_name()? in %s on line %d + +Deprecated: Function mysqli_get_charset() is deprecated since 8.6, did you mean mysqli_character_set_name()? in %s on line %d mysqli object is already closed done! diff --git a/ext/mysqli/tests/mysqli_stmt_execute_stored_proc.phpt b/ext/mysqli/tests/mysqli_stmt_execute_stored_proc.phpt index 935e541b7897..766b105dcb6c 100644 --- a/ext/mysqli/tests/mysqli_stmt_execute_stored_proc.phpt +++ b/ext/mysqli/tests/mysqli_stmt_execute_stored_proc.phpt @@ -24,7 +24,8 @@ if (mysqli_get_server_version($link) <= 50000) { if (!mysqli_query($link, 'DROP PROCEDURE IF EXISTS p')) printf("[009] [%d] %s.\n", mysqli_errno($link), mysqli_error($link)); - if (mysqli_real_query($link, 'CREATE PROCEDURE p(OUT ver_param VARCHAR(25)) BEGIN SELECT VERSION() INTO ver_param; END;')) { + // The test fails on MariaDB when the version string is too long, so keep it at least 50 + if (mysqli_real_query($link, 'CREATE PROCEDURE p(OUT ver_param VARCHAR(50)) BEGIN SELECT VERSION() INTO ver_param; END;')) { /* no result set, one output parameter */ if (!$stmt = mysqli_prepare($link, 'CALL p(@version)')) printf("[011] Cannot prepare CALL, [%d] %s\n", mysqli_errno($link), mysqli_error($link)); @@ -66,7 +67,7 @@ if (mysqli_get_server_version($link) <= 50000) { if (!mysqli_query($link, 'DROP PROCEDURE IF EXISTS p')) printf("[019] [%d] %s.\n", mysqli_errno($link), mysqli_error($link)); - if (mysqli_real_query($link, 'CREATE PROCEDURE p(OUT ver_param VARCHAR(25)) BEGIN SELECT VERSION() INTO ver_param; END;')) { + if (mysqli_real_query($link, 'CREATE PROCEDURE p(OUT ver_param VARCHAR(50)) BEGIN SELECT VERSION() INTO ver_param; END;')) { // no result set, one output parameter if (!$stmt = mysqli_prepare($link, 'CALL p(@version)')) printf("[020] Cannot prepare CALL, [%d] %s\n", mysqli_errno($link), mysqli_error($link)); diff --git a/ext/mysqli/tests/mysqli_stmt_get_result_metadata_fetch_field.phpt b/ext/mysqli/tests/mysqli_stmt_get_result_metadata_fetch_field.phpt index 600c8ae47993..4cf36a58591c 100644 --- a/ext/mysqli/tests/mysqli_stmt_get_result_metadata_fetch_field.phpt +++ b/ext/mysqli/tests/mysqli_stmt_get_result_metadata_fetch_field.phpt @@ -15,8 +15,6 @@ require_once 'skipifconnectfailure.inc'; if (!mysqli_set_charset($link, 'utf8')) printf("[%d] %s\n", mysqli_errno($link), mysqli_errno($link)); - $charsetInfo = mysqli_get_charset($link); - if (!($stmt = mysqli_stmt_init($link)) || !mysqli_stmt_prepare($stmt, "SELECT id, label, id + 1 as _id, concat(label, '_') ___label FROM test ORDER BY id ASC LIMIT 3") || !mysqli_stmt_execute($stmt)) @@ -42,9 +40,10 @@ require_once 'skipifconnectfailure.inc'; Label column, result set charset. All of the following columns are "too hot" - too server dependent */ - if ($field->length != $charsetInfo->max_length) { + $charMaxLength = 3; + if ($field->length != $charMaxLength) { printf("[005] Expecting length %d got %d\n", - $charsetInfo->max_length, $field->max_length); + $charMaxLength, $field->max_length); } } } From 969a50d9957bf6806452ae3f3434c6a30369cc1f Mon Sep 17 00:00:00 2001 From: Gina Peter Banyard Date: Mon, 10 Aug 2026 23:17:13 +0100 Subject: [PATCH 4/6] Update NEWS/UPGRADING for deprecations --- NEWS | 53 +++++++++++++++++++++++++++++++++++++++++++++++++++++ UPGRADING | 4 ++++ 2 files changed, 57 insertions(+) diff --git a/NEWS b/NEWS index 23f9fad2e3f4..e5aa1098ffe9 100644 --- a/NEWS +++ b/NEWS @@ -17,6 +17,12 @@ PHP NEWS -d error_include_args=On). (David Carlier) . Fixed GH-23121 (is_callable() wrongly accepts objects with no get_closure handler). (David Carlier) + . Passing a 3rd argument to define() is now deprecated. (Girgias) + . Naming a function readonly is now deprecated. (Girgias) + +- BZ2: + . Passing an object for the Bzip2 {de}compression stream filter is now + deprecated. Use get_object_vars() on the object instead. (Girgias) - Curl: . Improved cURL option validation errors to include the option name. @@ -60,6 +66,12 @@ PHP NEWS . Fixed IntlNumberRangeFormatter::format() crash when the formatting fails. (David Carlier) +- MbString: + . Passing objects to mb_convert_variables() is now deprecated. (Girgias) + +- MySQLi: + . The mysqli_get_charset() function is now deprecated. (Kamil Tekiela) + - PDO: . Fixed pdo_raise_impl_error() emitting a warning under ERRMODE_SILENT. (iliaal) @@ -91,6 +103,24 @@ PHP NEWS . Fixed segfault when comparing uninitialized SimpleXMLElement instances. (iliaal) +- SPL: + . The spl_classes() function is now deprecated, use + ReflectionExtension::getClassNames() instead. (Girgias) + . The spl_object_hash() function is now deprecated, use spl_object_id() + instead. (Girgias) + . The following ArrayIterator methods are now deprecated: + * ArrayIterator::getFlags() + * ArrayIterator::setFlags() + * ArrayIterator::asort() + * ArrayIterator::ksort() + * ArrayIterator::uasort() + * ArrayIterator::uksort() + * ArrayIterator::natsort() + * ArrayIterator::natcasesort() + * ArrayIterator::unserialize() + * ArrayIterator::serialize() + (Girgias) + - Standard: . Added the "filter.max_filter_count" stream context option for php://filter URLs. Using more than 16 filters without configuring this option is now @@ -108,11 +138,34 @@ PHP NEWS shutdown re-registration). (David Carlier) . Io\Poll\Context::wait() now takes a Time\Duration object as a timeout. (timwolla) + . Passing an object to array_walk{_recursive} is now deprecated. Use + get_object_vars() on the object instead. (Girgias) + . The is_double() function is now deprecated, use is_float() instead. + (Girgias) + . The is_long() and is_integer() functions are now deprecated, use is_int() + instead. (Girgias) + . The doubleval() function is now deprecated, use floatval() instead. + (Girgias) + . The strcoll() function is now deprecated, use Collator::compare() instead. + (Girgias) + . The SORT_LOCALE_STRING constant for the family of sort functions is now + deprecated, use one of the following functions instead: + * Collator::sort() + * Collator::asort() + * Collator::sort() + * Collator::sortWithSortKeys() + (Girgias) - XSL: . Fixed use-after-free when a DOMDocument subclass __clone() retains the stylesheet copy made by XSLTProcessor::importStylesheet(). (iliaal) +- Zlib + . Passing an object for the zlib deflate and inflate stream filter is now + deprecated. Use get_object_vars() on the object instead. (Girgias) + . Passing an object to the $option argument to deflate_init and inflate_init + is now deprecated. Use get_object_vars() on the object instead. (Girgias) + 30 Jul 2026, PHP 8.6.0alpha3 - Core: diff --git a/UPGRADING b/UPGRADING index 424e4189057c..0d32f455f32f 100644 --- a/UPGRADING +++ b/UPGRADING @@ -470,6 +470,10 @@ PHP 8.6 UPGRADE NOTES . Passing objects to mb_convert_variables() is now deprecated RFC: https://wiki.php.net/rfc/deprecations_php_8_6#passing_objects_for_vars_parameter_of_mb_convert_variables +- MySQLi: + . The mysqli_get_charset() function is now deprecated. + RFC: https://wiki.php.net/rfc/deprecations_php_8_6#deprecate_mysqli_get_charset + - SPL: . The spl_classes() function is now deprecated, use ReflectionExtension::getClassNames() instead. From f47cd216e9537301471d6af770998fe5fe5c784c Mon Sep 17 00:00:00 2001 From: Ilija Tovilo Date: Mon, 22 Sep 2025 23:27:07 +0200 Subject: [PATCH 5/6] Add stateless closure cache RFC: https://wiki.php.net/rfc/closure-optimizations#stateless_closure_caching Extracted from GH-19941. Sadly, @DanielEScherzer discovered an edge-case I did not consider: class Foo { public function instanceCall() { return $this; } public function test($c) { return array_map($c, [1]); } } $foo = new Foo(); var_dump($foo->test('Foo::instanceCall')); Here, the internal function array_map() can perform instance calls through a previous stack frame with just a named closure. Same with ['Foo', 'instanceCall']. That's quite esoteric, but this behavior should first be deprecated and removed before assuming it isn't being used. For the time being, merge only the latter half of the RFC. Code with all relevant functions properly annotated as static will get the full performance benefit. Closes GH-23203 --- NEWS | 1 + UPGRADING | 2 + Zend/Optimizer/compact_literals.c | 1 + .../pipe_optimization_004.phpt | 2 +- .../pipe_optimization_007.phpt | 2 +- .../pipe_optimization_008.phpt | 2 +- .../pipe_optimization_013.phpt | 2 +- Zend/zend_compile.c | 18 +++ Zend/zend_execute_API.c | 10 ++ Zend/zend_globals.h | 1 + Zend/zend_vm_def.h | 17 +- Zend/zend_vm_execute.h | 150 ++++++++++-------- Zend/zend_vm_handlers.h | 2 +- Zend/zend_vm_opcodes.c | 2 +- .../array_map_foreach_optimization_008.phpt | 2 +- ext/opcache/tests/gh19867.phpt | 4 +- 16 files changed, 145 insertions(+), 73 deletions(-) diff --git a/NEWS b/NEWS index e5aa1098ffe9..f60af7c1b769 100644 --- a/NEWS +++ b/NEWS @@ -19,6 +19,7 @@ PHP NEWS handler). (David Carlier) . Passing a 3rd argument to define() is now deprecated. (Girgias) . Naming a function readonly is now deprecated. (Girgias) + . Added stateless closure cache. (ilutov) - BZ2: . Passing an object for the Bzip2 {de}compression stream filter is now diff --git a/UPGRADING b/UPGRADING index 0d32f455f32f..779c95ae9530 100644 --- a/UPGRADING +++ b/UPGRADING @@ -792,6 +792,8 @@ PHP 8.6 UPGRADE NOTES . The TAILCALL VM is now enabled on Windows when compiling with Clang >= 19 x86_64. . The performance of ZTS builds has been improved. + . Added stateless closure cache. + RFC: https://wiki.php.net/rfc/closure-optimizations#stateless_closure_caching - DOM: . Made splitText() faster and consume less memory. diff --git a/Zend/Optimizer/compact_literals.c b/Zend/Optimizer/compact_literals.c index e43da5892b9f..8277092d76b3 100644 --- a/Zend/Optimizer/compact_literals.c +++ b/Zend/Optimizer/compact_literals.c @@ -756,6 +756,7 @@ void zend_optimizer_compact_literals(zend_op_array *op_array, zend_optimizer_ctx } break; case ZEND_CALLABLE_CONVERT: + case ZEND_DECLARE_LAMBDA_FUNCTION: if (opline->extended_value != (uint32_t)-1) { opline->extended_value = cache_size; cache_size += sizeof(void *); diff --git a/Zend/tests/partial_application/pipe_optimization_004.phpt b/Zend/tests/partial_application/pipe_optimization_004.phpt index 194f08a9c143..2d70493f9d69 100644 --- a/Zend/tests/partial_application/pipe_optimization_004.phpt +++ b/Zend/tests/partial_application/pipe_optimization_004.phpt @@ -70,7 +70,7 @@ $_main: ; (lines=3, args=0, vars=0, tmps=%d) ; (after optimizer) ; %s:1-10 -0000 T0 = DECLARE_LAMBDA_FUNCTION 0 +0000 T0 = DECLARE_LAMBDA_FUNCTION %d 0 0001 FREE T0 0002 RETURN int(1) diff --git a/Zend/tests/partial_application/pipe_optimization_007.phpt b/Zend/tests/partial_application/pipe_optimization_007.phpt index be8d773c44ce..03c98e26423c 100644 --- a/Zend/tests/partial_application/pipe_optimization_007.phpt +++ b/Zend/tests/partial_application/pipe_optimization_007.phpt @@ -70,7 +70,7 @@ $_main: ; (lines=3, args=0, vars=0, tmps=%d) ; (after optimizer) ; %s:1-10 -0000 T0 = DECLARE_LAMBDA_FUNCTION 0 +0000 T0 = DECLARE_LAMBDA_FUNCTION %d 0 0001 FREE T0 0002 RETURN int(1) diff --git a/Zend/tests/partial_application/pipe_optimization_008.phpt b/Zend/tests/partial_application/pipe_optimization_008.phpt index 96f8d88815ca..baa0f3d8d4ca 100644 --- a/Zend/tests/partial_application/pipe_optimization_008.phpt +++ b/Zend/tests/partial_application/pipe_optimization_008.phpt @@ -66,7 +66,7 @@ $_main: ; (lines=4, args=0, vars=1, tmps=%d) ; (after optimizer) ; %s:1-10 -0000 T1 = DECLARE_LAMBDA_FUNCTION 0 +0000 T1 = DECLARE_LAMBDA_FUNCTION %d 0 0001 BIND_LEXICAL T1 CV0($a) 0002 FREE T1 0003 RETURN int(1) diff --git a/Zend/tests/partial_application/pipe_optimization_013.phpt b/Zend/tests/partial_application/pipe_optimization_013.phpt index 6c2be6728e73..e4fb797657de 100644 --- a/Zend/tests/partial_application/pipe_optimization_013.phpt +++ b/Zend/tests/partial_application/pipe_optimization_013.phpt @@ -54,7 +54,7 @@ $_main: ; (lines=4, args=0, vars=1, tmps=%d) ; (after optimizer) ; %s:1-9 -0000 T1 = DECLARE_LAMBDA_FUNCTION 0 +0000 T1 = DECLARE_LAMBDA_FUNCTION %d 0 0001 BIND_LEXICAL T1 CV0($b) 0002 FREE T1 0003 RETURN int(1) diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c index cac60319a0af..6fd4df023520 100644 --- a/Zend/zend_compile.c +++ b/Zend/zend_compile.c @@ -8941,6 +8941,7 @@ static zend_string *zend_begin_func_decl(znode *result, zend_op_array *op_array, if (op_array->fn_flags & ZEND_ACC_CLOSURE) { opline = zend_emit_op_tmp(result, ZEND_DECLARE_LAMBDA_FUNCTION, NULL, NULL); opline->op2.num = func_ref; + opline->extended_value = (uint32_t)-1; } else { opline = get_next_op(); opline->opcode = ZEND_DECLARE_FUNCTION; @@ -9130,6 +9131,23 @@ static zend_op_array *zend_compile_func_decl_ex( zend_compile_stmt(stmt_ast); + if (decl->kind == ZEND_AST_CLOSURE || decl->kind == ZEND_AST_ARROW_FUNC) { + zend_op_array *declaring_op_array = orig_oparray_context.op_array; + + if ((op_array->fn_flags & ZEND_ACC_STATIC) + && !op_array->static_variables + /* Don't cache closures in main, as those would leak without a proper + * cleanup mechanism. */ + && declaring_op_array->function_name + && declaring_op_array->last) { + zend_op *declare_lambda_op = &declaring_op_array->opcodes[declaring_op_array->last - 1]; + if (declare_lambda_op->opcode == ZEND_DECLARE_LAMBDA_FUNCTION) { + declare_lambda_op->extended_value = declaring_op_array->cache_size; + declaring_op_array->cache_size += sizeof(void *); + } + } + } + if (is_method) { CG(zend_lineno) = decl->start_lineno; zend_check_magic_method_implementation( diff --git a/Zend/zend_execute_API.c b/Zend/zend_execute_API.c index 83ba5e7490cf..cc99a75a6ab9 100644 --- a/Zend/zend_execute_API.c +++ b/Zend/zend_execute_API.c @@ -203,6 +203,7 @@ void init_executor(void) /* {{{ */ zend_hash_init(&EG(callable_convert_cache), 8, NULL, ZVAL_PTR_DTOR, 0); zend_hash_init(&EG(partial_function_application_cache), 8, NULL, zend_partial_op_array_dtor, 0); + zend_stack_init(&EG(lambda_cache), sizeof(zend_object *)); EG(active) = 1; } @@ -268,6 +269,14 @@ void shutdown_destructors(void) /* {{{ */ } /* }}} */ +static void lambda_dtor(zend_object **closure_ptr) +{ + zend_object *closure = *closure_ptr; + if (GC_DELREF(closure) == 0) { + zend_objects_store_del(closure); + } +} + /* Free values held by the executor. */ ZEND_API void zend_shutdown_executor_values(bool fast_shutdown) { @@ -421,6 +430,7 @@ ZEND_API void zend_shutdown_executor_values(bool fast_shutdown) zend_hash_clean(&EG(callable_convert_cache)); zend_hash_clean(&EG(partial_function_application_cache)); + zend_stack_clean(&EG(lambda_cache), (void (*)(void *)) lambda_dtor, 1); #if ZEND_DEBUG if (!CG(unclean_shutdown)) { diff --git a/Zend/zend_globals.h b/Zend/zend_globals.h index 83360a2c96d2..4d5e300e2859 100644 --- a/Zend/zend_globals.h +++ b/Zend/zend_globals.h @@ -326,6 +326,7 @@ struct _zend_executor_globals { HashTable callable_convert_cache; HashTable partial_function_application_cache; + zend_stack lambda_cache; void *reserved[ZEND_MAX_RESERVED_RESOURCES]; }; diff --git a/Zend/zend_vm_def.h b/Zend/zend_vm_def.h index d89d460d9d9d..cf8072646ee4 100644 --- a/Zend/zend_vm_def.h +++ b/Zend/zend_vm_def.h @@ -8409,13 +8409,21 @@ ZEND_VM_HANDLER(210, ZEND_DECLARE_ATTRIBUTED_CONST, CONST, CONST) ZEND_VM_NEXT_OPCODE_EX(1, 2); } -ZEND_VM_HANDLER(142, ZEND_DECLARE_LAMBDA_FUNCTION, CONST, NUM) +ZEND_VM_HANDLER(142, ZEND_DECLARE_LAMBDA_FUNCTION, UNUSED, NUM, NUM|CACHE_SLOT) { USE_OPLINE zend_function *func; zval *object; zend_class_entry *called_scope; + if (opline->extended_value != (uint32_t)-1) { + zend_object *closure = CACHED_PTR(opline->extended_value); + if (closure) { + ZVAL_OBJ_COPY(EX_VAR(opline->result.var), closure); + ZEND_VM_NEXT_OPCODE(); + } + } + func = (zend_function *) EX(func)->op_array.dynamic_func_defs[opline->op2.num]; if (Z_TYPE(EX(This)) == IS_OBJECT) { called_scope = Z_OBJCE(EX(This)); @@ -8432,7 +8440,12 @@ ZEND_VM_HANDLER(142, ZEND_DECLARE_LAMBDA_FUNCTION, CONST, NUM) SAVE_OPLINE(); zend_create_closure(EX_VAR(opline->result.var), func, EX(func)->op_array.scope, called_scope, object); - + if (opline->extended_value != (uint32_t)-1) { + zend_object *closure = Z_OBJ_P(EX_VAR(opline->result.var)); + GC_ADDREF(closure); + CACHE_PTR(opline->extended_value, closure); + zend_stack_push(&EG(lambda_cache), &closure); + } ZEND_VM_NEXT_OPCODE(); } diff --git a/Zend/zend_vm_execute.h b/Zend/zend_vm_execute.h index 4c830f203bfb..58cd0ddf916e 100644 --- a/Zend/zend_vm_execute.h +++ b/Zend/zend_vm_execute.h @@ -5916,33 +5916,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_FUNC_CCONV ZEND_DECLARE_CLASS ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION(); } -static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_FUNC_CCONV ZEND_DECLARE_LAMBDA_FUNCTION_SPEC_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS) -{ - USE_OPLINE - zend_function *func; - zval *object; - zend_class_entry *called_scope; - - func = (zend_function *) EX(func)->op_array.dynamic_func_defs[opline->op2.num]; - if (Z_TYPE(EX(This)) == IS_OBJECT) { - called_scope = Z_OBJCE(EX(This)); - if (UNEXPECTED((func->common.fn_flags & ZEND_ACC_STATIC) || - (EX(func)->common.fn_flags & ZEND_ACC_STATIC))) { - object = NULL; - } else { - object = &EX(This); - } - } else { - called_scope = Z_CE(EX(This)); - object = NULL; - } - SAVE_OPLINE(); - zend_create_closure(EX_VAR(opline->result.var), func, - EX(func)->op_array.scope, called_scope, object); - - ZEND_VM_NEXT_OPCODE(); -} - static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_FUNC_CCONV ZEND_YIELD_FROM_SPEC_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS) { USE_OPLINE @@ -32928,6 +32901,46 @@ static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_FUNC_CCONV ZEND_CLONE_SPEC_UN ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION(); } +static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_FUNC_CCONV ZEND_DECLARE_LAMBDA_FUNCTION_SPEC_UNUSED_HANDLER(ZEND_OPCODE_HANDLER_ARGS) +{ + USE_OPLINE + zend_function *func; + zval *object; + zend_class_entry *called_scope; + + if (opline->extended_value != (uint32_t)-1) { + zend_object *closure = CACHED_PTR(opline->extended_value); + if (closure) { + ZVAL_OBJ_COPY(EX_VAR(opline->result.var), closure); + ZEND_VM_NEXT_OPCODE(); + } + } + + func = (zend_function *) EX(func)->op_array.dynamic_func_defs[opline->op2.num]; + if (Z_TYPE(EX(This)) == IS_OBJECT) { + called_scope = Z_OBJCE(EX(This)); + if (UNEXPECTED((func->common.fn_flags & ZEND_ACC_STATIC) || + (EX(func)->common.fn_flags & ZEND_ACC_STATIC))) { + object = NULL; + } else { + object = &EX(This); + } + } else { + called_scope = Z_CE(EX(This)); + object = NULL; + } + SAVE_OPLINE(); + zend_create_closure(EX_VAR(opline->result.var), func, + EX(func)->op_array.scope, called_scope, object); + if (opline->extended_value != (uint32_t)-1) { + zend_object *closure = Z_OBJ_P(EX_VAR(opline->result.var)); + GC_ADDREF(closure); + CACHE_PTR(opline->extended_value, closure); + zend_stack_push(&EG(lambda_cache), &closure); + } + ZEND_VM_NEXT_OPCODE(); +} + static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_FUNC_CCONV ZEND_FETCH_CLASS_NAME_SPEC_UNUSED_HANDLER(ZEND_OPCODE_HANDLER_ARGS) { uint32_t fetch_type; @@ -58748,33 +58761,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_CCONV ZEND_DECLARE_CLASS_SPEC ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION(); } -static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_CCONV ZEND_DECLARE_LAMBDA_FUNCTION_SPEC_CONST_TAILCALL_HANDLER(ZEND_OPCODE_HANDLER_ARGS) -{ - USE_OPLINE - zend_function *func; - zval *object; - zend_class_entry *called_scope; - - func = (zend_function *) EX(func)->op_array.dynamic_func_defs[opline->op2.num]; - if (Z_TYPE(EX(This)) == IS_OBJECT) { - called_scope = Z_OBJCE(EX(This)); - if (UNEXPECTED((func->common.fn_flags & ZEND_ACC_STATIC) || - (EX(func)->common.fn_flags & ZEND_ACC_STATIC))) { - object = NULL; - } else { - object = &EX(This); - } - } else { - called_scope = Z_CE(EX(This)); - object = NULL; - } - SAVE_OPLINE(); - zend_create_closure(EX_VAR(opline->result.var), func, - EX(func)->op_array.scope, called_scope, object); - - ZEND_VM_NEXT_OPCODE(); -} - static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_CCONV ZEND_YIELD_FROM_SPEC_CONST_TAILCALL_HANDLER(ZEND_OPCODE_HANDLER_ARGS) { USE_OPLINE @@ -85558,6 +85544,46 @@ static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_CCONV ZEND_CLONE_SPEC_UNUSED_ ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION(); } +static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_CCONV ZEND_DECLARE_LAMBDA_FUNCTION_SPEC_UNUSED_TAILCALL_HANDLER(ZEND_OPCODE_HANDLER_ARGS) +{ + USE_OPLINE + zend_function *func; + zval *object; + zend_class_entry *called_scope; + + if (opline->extended_value != (uint32_t)-1) { + zend_object *closure = CACHED_PTR(opline->extended_value); + if (closure) { + ZVAL_OBJ_COPY(EX_VAR(opline->result.var), closure); + ZEND_VM_NEXT_OPCODE(); + } + } + + func = (zend_function *) EX(func)->op_array.dynamic_func_defs[opline->op2.num]; + if (Z_TYPE(EX(This)) == IS_OBJECT) { + called_scope = Z_OBJCE(EX(This)); + if (UNEXPECTED((func->common.fn_flags & ZEND_ACC_STATIC) || + (EX(func)->common.fn_flags & ZEND_ACC_STATIC))) { + object = NULL; + } else { + object = &EX(This); + } + } else { + called_scope = Z_CE(EX(This)); + object = NULL; + } + SAVE_OPLINE(); + zend_create_closure(EX_VAR(opline->result.var), func, + EX(func)->op_array.scope, called_scope, object); + if (opline->extended_value != (uint32_t)-1) { + zend_object *closure = Z_OBJ_P(EX_VAR(opline->result.var)); + GC_ADDREF(closure); + CACHE_PTR(opline->extended_value, closure); + zend_stack_push(&EG(lambda_cache), &closure); + } + ZEND_VM_NEXT_OPCODE(); +} + static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_CCONV ZEND_FETCH_CLASS_NAME_SPEC_UNUSED_TAILCALL_HANDLER(ZEND_OPCODE_HANDLER_ARGS) { uint32_t fetch_type; @@ -109287,7 +109313,7 @@ ZEND_API void execute_ex(zend_execute_data *ex) (void*)&&ZEND_NULL_LABEL, (void*)&&ZEND_MAKE_REF_SPEC_CV_UNUSED_LABEL, (void*)&&ZEND_DECLARE_FUNCTION_SPEC_LABEL, - (void*)&&ZEND_DECLARE_LAMBDA_FUNCTION_SPEC_CONST_LABEL, + (void*)&&ZEND_DECLARE_LAMBDA_FUNCTION_SPEC_UNUSED_LABEL, (void*)&&ZEND_DECLARE_CONST_SPEC_CONST_CONST_LABEL, (void*)&&ZEND_DECLARE_CLASS_SPEC_CONST_LABEL, (void*)&&ZEND_DECLARE_CLASS_DELAYED_SPEC_CONST_CONST_LABEL, @@ -111298,11 +111324,6 @@ ZEND_API void execute_ex(zend_execute_data *ex) ZEND_DECLARE_CLASS_SPEC_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU); VM_TRACE_OP_END(ZEND_DECLARE_CLASS_SPEC_CONST) HYBRID_BREAK(); - HYBRID_CASE(ZEND_DECLARE_LAMBDA_FUNCTION_SPEC_CONST): - VM_TRACE(ZEND_DECLARE_LAMBDA_FUNCTION_SPEC_CONST) - ZEND_DECLARE_LAMBDA_FUNCTION_SPEC_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU); - VM_TRACE_OP_END(ZEND_DECLARE_LAMBDA_FUNCTION_SPEC_CONST) - HYBRID_BREAK(); HYBRID_CASE(ZEND_YIELD_FROM_SPEC_CONST): VM_TRACE(ZEND_YIELD_FROM_SPEC_CONST) ZEND_YIELD_FROM_SPEC_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU); @@ -114122,6 +114143,11 @@ ZEND_API void execute_ex(zend_execute_data *ex) ZEND_CLONE_SPEC_UNUSED_HANDLER(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU); VM_TRACE_OP_END(ZEND_CLONE_SPEC_UNUSED) HYBRID_BREAK(); + HYBRID_CASE(ZEND_DECLARE_LAMBDA_FUNCTION_SPEC_UNUSED): + VM_TRACE(ZEND_DECLARE_LAMBDA_FUNCTION_SPEC_UNUSED) + ZEND_DECLARE_LAMBDA_FUNCTION_SPEC_UNUSED_HANDLER(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU); + VM_TRACE_OP_END(ZEND_DECLARE_LAMBDA_FUNCTION_SPEC_UNUSED) + HYBRID_BREAK(); HYBRID_CASE(ZEND_FETCH_CLASS_NAME_SPEC_UNUSED): VM_TRACE(ZEND_FETCH_CLASS_NAME_SPEC_UNUSED) ZEND_FETCH_CLASS_NAME_SPEC_UNUSED_HANDLER(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU); @@ -118255,7 +118281,7 @@ void zend_vm_init(void) ZEND_NULL_HANDLER, ZEND_MAKE_REF_SPEC_CV_UNUSED_HANDLER, ZEND_DECLARE_FUNCTION_SPEC_HANDLER, - ZEND_DECLARE_LAMBDA_FUNCTION_SPEC_CONST_HANDLER, + ZEND_DECLARE_LAMBDA_FUNCTION_SPEC_UNUSED_HANDLER, ZEND_DECLARE_CONST_SPEC_CONST_CONST_HANDLER, ZEND_DECLARE_CLASS_SPEC_CONST_HANDLER, ZEND_DECLARE_CLASS_DELAYED_SPEC_CONST_CONST_HANDLER, @@ -121743,7 +121769,7 @@ void zend_vm_init(void) ZEND_NULL_TAILCALL_HANDLER, ZEND_MAKE_REF_SPEC_CV_UNUSED_TAILCALL_HANDLER, ZEND_DECLARE_FUNCTION_SPEC_TAILCALL_HANDLER, - ZEND_DECLARE_LAMBDA_FUNCTION_SPEC_CONST_TAILCALL_HANDLER, + ZEND_DECLARE_LAMBDA_FUNCTION_SPEC_UNUSED_TAILCALL_HANDLER, ZEND_DECLARE_CONST_SPEC_CONST_CONST_TAILCALL_HANDLER, ZEND_DECLARE_CLASS_SPEC_CONST_TAILCALL_HANDLER, ZEND_DECLARE_CLASS_DELAYED_SPEC_CONST_CONST_TAILCALL_HANDLER, diff --git a/Zend/zend_vm_handlers.h b/Zend/zend_vm_handlers.h index 503a6d25634b..4586fd861021 100644 --- a/Zend/zend_vm_handlers.h +++ b/Zend/zend_vm_handlers.h @@ -910,7 +910,7 @@ _(2282, ZEND_MAKE_REF_SPEC_VAR_UNUSED) \ _(2284, ZEND_MAKE_REF_SPEC_CV_UNUSED) \ _(2285, ZEND_DECLARE_FUNCTION_SPEC) \ - _(2286, ZEND_DECLARE_LAMBDA_FUNCTION_SPEC_CONST) \ + _(2286, ZEND_DECLARE_LAMBDA_FUNCTION_SPEC_UNUSED) \ _(2287, ZEND_DECLARE_CONST_SPEC_CONST_CONST) \ _(2288, ZEND_DECLARE_CLASS_SPEC_CONST) \ _(2289, ZEND_DECLARE_CLASS_DELAYED_SPEC_CONST_CONST) \ diff --git a/Zend/zend_vm_opcodes.c b/Zend/zend_vm_opcodes.c index f9b30edb5e9b..cc14862ebedd 100644 --- a/Zend/zend_vm_opcodes.c +++ b/Zend/zend_vm_opcodes.c @@ -381,7 +381,7 @@ static uint32_t zend_vm_opcodes_flags[214] = { 0x00000000, 0x00000101, 0x00001000, - 0x00001003, + 0x01041001, 0x00000303, 0x00000003, 0x00000303, diff --git a/ext/opcache/tests/array_map_foreach_optimization_008.phpt b/ext/opcache/tests/array_map_foreach_optimization_008.phpt index 36c80b460e88..e90980922cab 100644 --- a/ext/opcache/tests/array_map_foreach_optimization_008.phpt +++ b/ext/opcache/tests/array_map_foreach_optimization_008.phpt @@ -56,7 +56,7 @@ $_main: ; (lines=4, args=0, vars=1, tmps=%d) ; (after optimizer) ; %s:1-9 -0000 T1 = DECLARE_LAMBDA_FUNCTION 0 +0000 T1 = DECLARE_LAMBDA_FUNCTION %d 0 0001 BIND_LEXICAL T1 CV0($n) 0002 FREE T1 0003 RETURN int(1) diff --git a/ext/opcache/tests/gh19867.phpt b/ext/opcache/tests/gh19867.phpt index 486a366722da..51b2aaeaf459 100644 --- a/ext/opcache/tests/gh19867.phpt +++ b/ext/opcache/tests/gh19867.phpt @@ -15,7 +15,7 @@ $_main: ; (lines=%d, args=0, vars=%d, tmps=%d) ; (after optimizer) ; %s -0000 T0 = DECLARE_LAMBDA_FUNCTION 0 +0000 T0 = DECLARE_LAMBDA_FUNCTION %d 0 0001 FREE T0 0002 RETURN int(1) @@ -23,7 +23,7 @@ $_main: ; (lines=%d, args=0, vars=%d, tmps=%d) ; (after optimizer) ; %s -0000 T0 = DECLARE_LAMBDA_FUNCTION 0 +0000 T0 = DECLARE_LAMBDA_FUNCTION %d 0 0001 RETURN T0 {closure:%s:%d}: From 4226d16a6f60162f8eaa7a1f523901c22d6a34a7 Mon Sep 17 00:00:00 2001 From: Ilija Tovilo Date: Tue, 11 Aug 2026 00:31:55 +0200 Subject: [PATCH 6/6] [skip ci] Fix NEWS section order --- NEWS | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/NEWS b/NEWS index f60af7c1b769..35d78d928995 100644 --- a/NEWS +++ b/NEWS @@ -2,10 +2,6 @@ PHP NEWS ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| ?? ??? ????, PHP 8.6.0beta1 -- Streams: - . Fixed file_put_contents() LOCK_EX early return leaking stream error - operation depth. (iliaal) - - Core: . Deprecated "namespace" as a class constant name. (NickSdot) . Changed run-tests.php to run in parallel by default, using up to 10 @@ -157,6 +153,10 @@ PHP NEWS * Collator::sortWithSortKeys() (Girgias) +- Streams: + . Fixed file_put_contents() LOCK_EX early return leaking stream error + operation depth. (iliaal) + - XSL: . Fixed use-after-free when a DOMDocument subclass __clone() retains the stylesheet copy made by XSLTProcessor::importStylesheet(). (iliaal)