From 2d10ae2f9899b160909456034505558f5eb4b82f Mon Sep 17 00:00:00 2001 From: Gina Peter Banyard Date: Mon, 5 Jan 2026 18:39:07 +0100 Subject: [PATCH 1/9] ext/spl: move autoloading tests to subfolder --- ext/spl/tests/{ => autoloading}/bug38325.phpt | 0 ext/spl/tests/{ => autoloading}/bug40091.phpt | 0 ext/spl/tests/{ => autoloading}/bug44144.phpt | 0 ext/spl/tests/{ => autoloading}/bug48023.phpt | 0 ext/spl/tests/{ => autoloading}/bug48493.phpt | 0 ext/spl/tests/{ => autoloading}/bug52339.phpt | 0 ext/spl/tests/{ => autoloading}/bug61697.phpt | 0 ext/spl/tests/{ => autoloading}/bug65006.phpt | 0 ext/spl/tests/{ => autoloading}/bug71202.phpt | 0 ext/spl/tests/{ => autoloading}/bug71204.phpt | 0 ext/spl/tests/{ => autoloading}/bug71236.phpt | 0 ext/spl/tests/{ => autoloading}/bug73896.phpt | 0 ext/spl/tests/{ => autoloading}/bug74372.phpt | 0 ext/spl/tests/{ => autoloading}/bug75049.phpt | 0 ext/spl/tests/{ => autoloading}/gh10011.phpt | 0 ext/spl/tests/{ => autoloading}/spl_autoload_001.phpt | 0 ext/spl/tests/{ => autoloading}/spl_autoload_002.phpt | 0 ext/spl/tests/{ => autoloading}/spl_autoload_003.phpt | 0 ext/spl/tests/{ => autoloading}/spl_autoload_004.phpt | 0 ext/spl/tests/{ => autoloading}/spl_autoload_005.phpt | 0 ext/spl/tests/{ => autoloading}/spl_autoload_006.phpt | 0 ext/spl/tests/{ => autoloading}/spl_autoload_007.phpt | 0 ext/spl/tests/{ => autoloading}/spl_autoload_008.phpt | 0 ext/spl/tests/{ => autoloading}/spl_autoload_009.phpt | 0 ext/spl/tests/{ => autoloading}/spl_autoload_010.phpt | 0 ext/spl/tests/{ => autoloading}/spl_autoload_011.phpt | 0 ext/spl/tests/{ => autoloading}/spl_autoload_012.phpt | 0 ext/spl/tests/{ => autoloading}/spl_autoload_013.phpt | 0 ext/spl/tests/{ => autoloading}/spl_autoload_014.phpt | 0 ext/spl/tests/{ => autoloading}/spl_autoload_bug48541.phpt | 0 ext/spl/tests/{ => autoloading}/spl_autoload_call_basic.phpt | 0 ext/spl/tests/{ => autoloading}/spl_autoload_called_scope.phpt | 0 ...spl_autoload_throw_with_spl_autoloader_call_as_autoloader.phpt | 0 .../spl_autoload_unregister_without_registrations.phpt | 0 .../{ => autoloading}/spl_autoload_warn_on_false_do_throw.phpt | 0 ext/spl/tests/{ => autoloading}/testclass | 0 ext/spl/tests/{ => autoloading}/testclass.class.inc | 0 ext/spl/tests/{ => autoloading}/testclass.inc | 0 ext/spl/tests/{ => autoloading}/testclass.php.inc | 0 39 files changed, 0 insertions(+), 0 deletions(-) rename ext/spl/tests/{ => autoloading}/bug38325.phpt (100%) rename ext/spl/tests/{ => autoloading}/bug40091.phpt (100%) rename ext/spl/tests/{ => autoloading}/bug44144.phpt (100%) rename ext/spl/tests/{ => autoloading}/bug48023.phpt (100%) rename ext/spl/tests/{ => autoloading}/bug48493.phpt (100%) rename ext/spl/tests/{ => autoloading}/bug52339.phpt (100%) rename ext/spl/tests/{ => autoloading}/bug61697.phpt (100%) rename ext/spl/tests/{ => autoloading}/bug65006.phpt (100%) rename ext/spl/tests/{ => autoloading}/bug71202.phpt (100%) rename ext/spl/tests/{ => autoloading}/bug71204.phpt (100%) rename ext/spl/tests/{ => autoloading}/bug71236.phpt (100%) rename ext/spl/tests/{ => autoloading}/bug73896.phpt (100%) rename ext/spl/tests/{ => autoloading}/bug74372.phpt (100%) rename ext/spl/tests/{ => autoloading}/bug75049.phpt (100%) rename ext/spl/tests/{ => autoloading}/gh10011.phpt (100%) rename ext/spl/tests/{ => autoloading}/spl_autoload_001.phpt (100%) rename ext/spl/tests/{ => autoloading}/spl_autoload_002.phpt (100%) rename ext/spl/tests/{ => autoloading}/spl_autoload_003.phpt (100%) rename ext/spl/tests/{ => autoloading}/spl_autoload_004.phpt (100%) rename ext/spl/tests/{ => autoloading}/spl_autoload_005.phpt (100%) rename ext/spl/tests/{ => autoloading}/spl_autoload_006.phpt (100%) rename ext/spl/tests/{ => autoloading}/spl_autoload_007.phpt (100%) rename ext/spl/tests/{ => autoloading}/spl_autoload_008.phpt (100%) rename ext/spl/tests/{ => autoloading}/spl_autoload_009.phpt (100%) rename ext/spl/tests/{ => autoloading}/spl_autoload_010.phpt (100%) rename ext/spl/tests/{ => autoloading}/spl_autoload_011.phpt (100%) rename ext/spl/tests/{ => autoloading}/spl_autoload_012.phpt (100%) rename ext/spl/tests/{ => autoloading}/spl_autoload_013.phpt (100%) rename ext/spl/tests/{ => autoloading}/spl_autoload_014.phpt (100%) rename ext/spl/tests/{ => autoloading}/spl_autoload_bug48541.phpt (100%) rename ext/spl/tests/{ => autoloading}/spl_autoload_call_basic.phpt (100%) rename ext/spl/tests/{ => autoloading}/spl_autoload_called_scope.phpt (100%) rename ext/spl/tests/{ => autoloading}/spl_autoload_throw_with_spl_autoloader_call_as_autoloader.phpt (100%) rename ext/spl/tests/{ => autoloading}/spl_autoload_unregister_without_registrations.phpt (100%) rename ext/spl/tests/{ => autoloading}/spl_autoload_warn_on_false_do_throw.phpt (100%) rename ext/spl/tests/{ => autoloading}/testclass (100%) rename ext/spl/tests/{ => autoloading}/testclass.class.inc (100%) rename ext/spl/tests/{ => autoloading}/testclass.inc (100%) rename ext/spl/tests/{ => autoloading}/testclass.php.inc (100%) diff --git a/ext/spl/tests/bug38325.phpt b/ext/spl/tests/autoloading/bug38325.phpt similarity index 100% rename from ext/spl/tests/bug38325.phpt rename to ext/spl/tests/autoloading/bug38325.phpt diff --git a/ext/spl/tests/bug40091.phpt b/ext/spl/tests/autoloading/bug40091.phpt similarity index 100% rename from ext/spl/tests/bug40091.phpt rename to ext/spl/tests/autoloading/bug40091.phpt diff --git a/ext/spl/tests/bug44144.phpt b/ext/spl/tests/autoloading/bug44144.phpt similarity index 100% rename from ext/spl/tests/bug44144.phpt rename to ext/spl/tests/autoloading/bug44144.phpt diff --git a/ext/spl/tests/bug48023.phpt b/ext/spl/tests/autoloading/bug48023.phpt similarity index 100% rename from ext/spl/tests/bug48023.phpt rename to ext/spl/tests/autoloading/bug48023.phpt diff --git a/ext/spl/tests/bug48493.phpt b/ext/spl/tests/autoloading/bug48493.phpt similarity index 100% rename from ext/spl/tests/bug48493.phpt rename to ext/spl/tests/autoloading/bug48493.phpt diff --git a/ext/spl/tests/bug52339.phpt b/ext/spl/tests/autoloading/bug52339.phpt similarity index 100% rename from ext/spl/tests/bug52339.phpt rename to ext/spl/tests/autoloading/bug52339.phpt diff --git a/ext/spl/tests/bug61697.phpt b/ext/spl/tests/autoloading/bug61697.phpt similarity index 100% rename from ext/spl/tests/bug61697.phpt rename to ext/spl/tests/autoloading/bug61697.phpt diff --git a/ext/spl/tests/bug65006.phpt b/ext/spl/tests/autoloading/bug65006.phpt similarity index 100% rename from ext/spl/tests/bug65006.phpt rename to ext/spl/tests/autoloading/bug65006.phpt diff --git a/ext/spl/tests/bug71202.phpt b/ext/spl/tests/autoloading/bug71202.phpt similarity index 100% rename from ext/spl/tests/bug71202.phpt rename to ext/spl/tests/autoloading/bug71202.phpt diff --git a/ext/spl/tests/bug71204.phpt b/ext/spl/tests/autoloading/bug71204.phpt similarity index 100% rename from ext/spl/tests/bug71204.phpt rename to ext/spl/tests/autoloading/bug71204.phpt diff --git a/ext/spl/tests/bug71236.phpt b/ext/spl/tests/autoloading/bug71236.phpt similarity index 100% rename from ext/spl/tests/bug71236.phpt rename to ext/spl/tests/autoloading/bug71236.phpt diff --git a/ext/spl/tests/bug73896.phpt b/ext/spl/tests/autoloading/bug73896.phpt similarity index 100% rename from ext/spl/tests/bug73896.phpt rename to ext/spl/tests/autoloading/bug73896.phpt diff --git a/ext/spl/tests/bug74372.phpt b/ext/spl/tests/autoloading/bug74372.phpt similarity index 100% rename from ext/spl/tests/bug74372.phpt rename to ext/spl/tests/autoloading/bug74372.phpt diff --git a/ext/spl/tests/bug75049.phpt b/ext/spl/tests/autoloading/bug75049.phpt similarity index 100% rename from ext/spl/tests/bug75049.phpt rename to ext/spl/tests/autoloading/bug75049.phpt diff --git a/ext/spl/tests/gh10011.phpt b/ext/spl/tests/autoloading/gh10011.phpt similarity index 100% rename from ext/spl/tests/gh10011.phpt rename to ext/spl/tests/autoloading/gh10011.phpt diff --git a/ext/spl/tests/spl_autoload_001.phpt b/ext/spl/tests/autoloading/spl_autoload_001.phpt similarity index 100% rename from ext/spl/tests/spl_autoload_001.phpt rename to ext/spl/tests/autoloading/spl_autoload_001.phpt diff --git a/ext/spl/tests/spl_autoload_002.phpt b/ext/spl/tests/autoloading/spl_autoload_002.phpt similarity index 100% rename from ext/spl/tests/spl_autoload_002.phpt rename to ext/spl/tests/autoloading/spl_autoload_002.phpt diff --git a/ext/spl/tests/spl_autoload_003.phpt b/ext/spl/tests/autoloading/spl_autoload_003.phpt similarity index 100% rename from ext/spl/tests/spl_autoload_003.phpt rename to ext/spl/tests/autoloading/spl_autoload_003.phpt diff --git a/ext/spl/tests/spl_autoload_004.phpt b/ext/spl/tests/autoloading/spl_autoload_004.phpt similarity index 100% rename from ext/spl/tests/spl_autoload_004.phpt rename to ext/spl/tests/autoloading/spl_autoload_004.phpt diff --git a/ext/spl/tests/spl_autoload_005.phpt b/ext/spl/tests/autoloading/spl_autoload_005.phpt similarity index 100% rename from ext/spl/tests/spl_autoload_005.phpt rename to ext/spl/tests/autoloading/spl_autoload_005.phpt diff --git a/ext/spl/tests/spl_autoload_006.phpt b/ext/spl/tests/autoloading/spl_autoload_006.phpt similarity index 100% rename from ext/spl/tests/spl_autoload_006.phpt rename to ext/spl/tests/autoloading/spl_autoload_006.phpt diff --git a/ext/spl/tests/spl_autoload_007.phpt b/ext/spl/tests/autoloading/spl_autoload_007.phpt similarity index 100% rename from ext/spl/tests/spl_autoload_007.phpt rename to ext/spl/tests/autoloading/spl_autoload_007.phpt diff --git a/ext/spl/tests/spl_autoload_008.phpt b/ext/spl/tests/autoloading/spl_autoload_008.phpt similarity index 100% rename from ext/spl/tests/spl_autoload_008.phpt rename to ext/spl/tests/autoloading/spl_autoload_008.phpt diff --git a/ext/spl/tests/spl_autoload_009.phpt b/ext/spl/tests/autoloading/spl_autoload_009.phpt similarity index 100% rename from ext/spl/tests/spl_autoload_009.phpt rename to ext/spl/tests/autoloading/spl_autoload_009.phpt diff --git a/ext/spl/tests/spl_autoload_010.phpt b/ext/spl/tests/autoloading/spl_autoload_010.phpt similarity index 100% rename from ext/spl/tests/spl_autoload_010.phpt rename to ext/spl/tests/autoloading/spl_autoload_010.phpt diff --git a/ext/spl/tests/spl_autoload_011.phpt b/ext/spl/tests/autoloading/spl_autoload_011.phpt similarity index 100% rename from ext/spl/tests/spl_autoload_011.phpt rename to ext/spl/tests/autoloading/spl_autoload_011.phpt diff --git a/ext/spl/tests/spl_autoload_012.phpt b/ext/spl/tests/autoloading/spl_autoload_012.phpt similarity index 100% rename from ext/spl/tests/spl_autoload_012.phpt rename to ext/spl/tests/autoloading/spl_autoload_012.phpt diff --git a/ext/spl/tests/spl_autoload_013.phpt b/ext/spl/tests/autoloading/spl_autoload_013.phpt similarity index 100% rename from ext/spl/tests/spl_autoload_013.phpt rename to ext/spl/tests/autoloading/spl_autoload_013.phpt diff --git a/ext/spl/tests/spl_autoload_014.phpt b/ext/spl/tests/autoloading/spl_autoload_014.phpt similarity index 100% rename from ext/spl/tests/spl_autoload_014.phpt rename to ext/spl/tests/autoloading/spl_autoload_014.phpt diff --git a/ext/spl/tests/spl_autoload_bug48541.phpt b/ext/spl/tests/autoloading/spl_autoload_bug48541.phpt similarity index 100% rename from ext/spl/tests/spl_autoload_bug48541.phpt rename to ext/spl/tests/autoloading/spl_autoload_bug48541.phpt diff --git a/ext/spl/tests/spl_autoload_call_basic.phpt b/ext/spl/tests/autoloading/spl_autoload_call_basic.phpt similarity index 100% rename from ext/spl/tests/spl_autoload_call_basic.phpt rename to ext/spl/tests/autoloading/spl_autoload_call_basic.phpt diff --git a/ext/spl/tests/spl_autoload_called_scope.phpt b/ext/spl/tests/autoloading/spl_autoload_called_scope.phpt similarity index 100% rename from ext/spl/tests/spl_autoload_called_scope.phpt rename to ext/spl/tests/autoloading/spl_autoload_called_scope.phpt diff --git a/ext/spl/tests/spl_autoload_throw_with_spl_autoloader_call_as_autoloader.phpt b/ext/spl/tests/autoloading/spl_autoload_throw_with_spl_autoloader_call_as_autoloader.phpt similarity index 100% rename from ext/spl/tests/spl_autoload_throw_with_spl_autoloader_call_as_autoloader.phpt rename to ext/spl/tests/autoloading/spl_autoload_throw_with_spl_autoloader_call_as_autoloader.phpt diff --git a/ext/spl/tests/spl_autoload_unregister_without_registrations.phpt b/ext/spl/tests/autoloading/spl_autoload_unregister_without_registrations.phpt similarity index 100% rename from ext/spl/tests/spl_autoload_unregister_without_registrations.phpt rename to ext/spl/tests/autoloading/spl_autoload_unregister_without_registrations.phpt diff --git a/ext/spl/tests/spl_autoload_warn_on_false_do_throw.phpt b/ext/spl/tests/autoloading/spl_autoload_warn_on_false_do_throw.phpt similarity index 100% rename from ext/spl/tests/spl_autoload_warn_on_false_do_throw.phpt rename to ext/spl/tests/autoloading/spl_autoload_warn_on_false_do_throw.phpt diff --git a/ext/spl/tests/testclass b/ext/spl/tests/autoloading/testclass similarity index 100% rename from ext/spl/tests/testclass rename to ext/spl/tests/autoloading/testclass diff --git a/ext/spl/tests/testclass.class.inc b/ext/spl/tests/autoloading/testclass.class.inc similarity index 100% rename from ext/spl/tests/testclass.class.inc rename to ext/spl/tests/autoloading/testclass.class.inc diff --git a/ext/spl/tests/testclass.inc b/ext/spl/tests/autoloading/testclass.inc similarity index 100% rename from ext/spl/tests/testclass.inc rename to ext/spl/tests/autoloading/testclass.inc diff --git a/ext/spl/tests/testclass.php.inc b/ext/spl/tests/autoloading/testclass.php.inc similarity index 100% rename from ext/spl/tests/testclass.php.inc rename to ext/spl/tests/autoloading/testclass.php.inc From 0cd648a42e760489bf12a6042c84d4dd8631f494 Mon Sep 17 00:00:00 2001 From: Gina Peter Banyard Date: Mon, 5 Jan 2026 17:38:47 +0100 Subject: [PATCH 2/9] ext/spl: refactor spl_autoload() Remove unused parameter Use zend_string_concat2() API Use size_t for ext_len parameter type Return bool instead of int --- ext/spl/php_spl.c | 37 ++++++++++++++----------------------- 1 file changed, 14 insertions(+), 23 deletions(-) diff --git a/ext/spl/php_spl.c b/ext/spl/php_spl.c index 0acfcbc5c16d0..834058a993f9f 100644 --- a/ext/spl/php_spl.c +++ b/ext/spl/php_spl.c @@ -34,7 +34,6 @@ #include "spl_heap.h" #include "zend_exceptions.h" #include "zend_interfaces.h" -#include "main/snprintf.h" ZEND_TLS zend_string *spl_autoload_extensions; ZEND_TLS HashTable *spl_autoload_functions; @@ -224,21 +223,19 @@ PHP_FUNCTION(spl_classes) } /* }}} */ -static int spl_autoload(zend_string *class_name, zend_string *lc_name, const char *ext, int ext_len) /* {{{ */ +static bool spl_autoload(zend_string *lc_name, const char *ext, size_t ext_len) /* {{{ */ { zend_string *class_file; zval dummy; zend_file_handle file_handle; - zend_op_array *new_op_array; zval result; - int ret; - class_file = zend_strpprintf(0, "%s%.*s", ZSTR_VAL(lc_name), ext_len, ext); + class_file = zend_string_concat2(ZSTR_VAL(lc_name), ZSTR_LEN(lc_name), ext, ext_len); #if DEFAULT_SLASH != '\\' { char *ptr = ZSTR_VAL(class_file); - char *end = ptr + ZSTR_LEN(class_file); + const char *end = ptr + ZSTR_LEN(class_file); while ((ptr = memchr(ptr, '\\', (end - ptr))) != NULL) { *ptr = DEFAULT_SLASH; @@ -246,22 +243,20 @@ static int spl_autoload(zend_string *class_name, zend_string *lc_name, const cha } #endif + bool ret = false; zend_stream_init_filename_ex(&file_handle, class_file); - ret = php_stream_open_for_zend_ex(&file_handle, USE_PATH|STREAM_OPEN_FOR_INCLUDE); - - if (ret == SUCCESS) { + if (php_stream_open_for_zend_ex(&file_handle, USE_PATH|STREAM_OPEN_FOR_INCLUDE) == SUCCESS) { zend_string *opened_path; if (!file_handle.opened_path) { file_handle.opened_path = zend_string_copy(class_file); } opened_path = zend_string_copy(file_handle.opened_path); ZVAL_NULL(&dummy); + zend_op_array *new_op_array = NULL; if (zend_hash_add(&EG(included_files), opened_path, &dummy)) { new_op_array = zend_compile_file(&file_handle, ZEND_REQUIRE); - } else { - new_op_array = NULL; } - zend_string_release_ex(opened_path, 0); + zend_string_release_ex(opened_path, false); if (new_op_array) { uint32_t orig_jit_trace_num = EG(jit_trace_num); @@ -271,24 +266,20 @@ static int spl_autoload(zend_string *class_name, zend_string *lc_name, const cha destroy_op_array(new_op_array); efree(new_op_array); - if (!EG(exception)) { - zval_ptr_dtor(&result); - } + zval_ptr_dtor(&result); - zend_destroy_file_handle(&file_handle); - zend_string_release(class_file); - return zend_hash_exists(EG(class_table), lc_name); + ret = zend_hash_exists(EG(class_table), lc_name); } } zend_destroy_file_handle(&file_handle); zend_string_release(class_file); - return 0; + return ret; } /* }}} */ /* {{{ Default autoloader implementation */ PHP_FUNCTION(spl_autoload) { - int pos_len, pos1_len; + size_t pos_len, pos1_len; char *pos, *pos1; zend_string *class_name, *lc_name, *file_exts = NULL; @@ -305,18 +296,18 @@ PHP_FUNCTION(spl_autoload) pos_len = sizeof(SPL_DEFAULT_FILE_EXTENSIONS) - 1; } else { pos = ZSTR_VAL(file_exts); - pos_len = (int)ZSTR_LEN(file_exts); + pos_len = ZSTR_LEN(file_exts); } lc_name = zend_string_tolower(class_name); while (pos && *pos && !EG(exception)) { pos1 = strchr(pos, ','); if (pos1) { - pos1_len = (int)(pos1 - pos); + pos1_len = (size_t)(pos1 - pos); } else { pos1_len = pos_len; } - if (spl_autoload(class_name, lc_name, pos, pos1_len)) { + if (spl_autoload(lc_name, pos, pos1_len)) { break; /* loaded */ } pos = pos1 ? pos1 + 1 : NULL; From 8326ad029d5220d8d413d8e59a15b8a44f39bec0 Mon Sep 17 00:00:00 2001 From: Gina Peter Banyard Date: Mon, 5 Jan 2026 18:39:30 +0100 Subject: [PATCH 3/9] ext/spl: refactor autoloading to use newer FCC APIs --- ext/spl/php_spl.c | 170 +++++++++++----------------------------------- 1 file changed, 41 insertions(+), 129 deletions(-) diff --git a/ext/spl/php_spl.c b/ext/spl/php_spl.c index 834058a993f9f..bfe8e6f29017f 100644 --- a/ext/spl/php_spl.c +++ b/ext/spl/php_spl.c @@ -340,67 +340,11 @@ PHP_FUNCTION(spl_autoload_extensions) } } /* }}} */ -typedef struct { - zend_function *func_ptr; - zend_object *obj; - zend_object *closure; - zend_class_entry *ce; -} autoload_func_info; - -static void autoload_func_info_destroy(autoload_func_info *alfi) { - if (alfi->obj) { - zend_object_release(alfi->obj); - } - if (alfi->func_ptr && - UNEXPECTED(alfi->func_ptr->common.fn_flags & ZEND_ACC_CALL_VIA_TRAMPOLINE)) { - zend_string_release_ex(alfi->func_ptr->common.function_name, 0); - zend_free_trampoline(alfi->func_ptr); - } - if (alfi->closure) { - zend_object_release(alfi->closure); - } - efree(alfi); -} - static void autoload_func_info_zval_dtor(zval *element) { - autoload_func_info_destroy(Z_PTR_P(element)); -} - -static autoload_func_info *autoload_func_info_from_fci( - zend_fcall_info *fci, zend_fcall_info_cache *fcc) { - autoload_func_info *alfi = emalloc(sizeof(autoload_func_info)); - alfi->ce = fcc->calling_scope; - alfi->func_ptr = fcc->function_handler; - alfi->obj = fcc->object; - if (alfi->obj) { - GC_ADDREF(alfi->obj); - } - if (Z_TYPE(fci->function_name) == IS_OBJECT) { - alfi->closure = Z_OBJ(fci->function_name); - GC_ADDREF(alfi->closure); - } else { - alfi->closure = NULL; - } - return alfi; -} - -static bool autoload_func_info_equals( - const autoload_func_info *alfi1, const autoload_func_info *alfi2) { - if (UNEXPECTED( - (alfi1->func_ptr->common.fn_flags & ZEND_ACC_CALL_VIA_TRAMPOLINE) && - (alfi2->func_ptr->common.fn_flags & ZEND_ACC_CALL_VIA_TRAMPOLINE) - )) { - return alfi1->obj == alfi2->obj - && alfi1->ce == alfi2->ce - && alfi1->closure == alfi2->closure - && zend_string_equals(alfi1->func_ptr->common.function_name, alfi2->func_ptr->common.function_name) - ; - } - return alfi1->func_ptr == alfi2->func_ptr - && alfi1->obj == alfi2->obj - && alfi1->ce == alfi2->ce - && alfi1->closure == alfi2->closure; + zend_fcall_info_cache *fcc = Z_PTR_P(element); + zend_fcc_dtor(fcc); + efree(fcc); } static zend_class_entry *spl_perform_autoload(zend_string *class_name, zend_string *lc_name) { @@ -412,34 +356,27 @@ static zend_class_entry *spl_perform_autoload(zend_string *class_name, zend_stri * because autoloaders may be added/removed during autoloading. */ HashPosition pos; zend_hash_internal_pointer_reset_ex(spl_autoload_functions, &pos); - while (1) { - autoload_func_info *alfi = + + zval zname; + ZVAL_STR(&zname, class_name); + while (true) { + zend_fcall_info_cache *fcc = zend_hash_get_current_data_ptr_ex(spl_autoload_functions, &pos); - if (!alfi) { + if (!fcc) { break; } - zend_function *func = alfi->func_ptr; - if (UNEXPECTED(func->common.fn_flags & ZEND_ACC_CALL_VIA_TRAMPOLINE)) { - func = emalloc(sizeof(zend_op_array)); - memcpy(func, alfi->func_ptr, sizeof(zend_op_array)); - zend_string_addref(func->op_array.function_name); - } - - zval param; - ZVAL_STR(¶m, class_name); - zend_call_known_function(func, alfi->obj, alfi->ce, NULL, 1, ¶m, NULL); - if (EG(exception)) { + zend_call_known_fcc(fcc, NULL, 1, &zname, NULL); + if (UNEXPECTED(EG(exception))) { break; } - if (ZSTR_HAS_CE_CACHE(class_name) && ZSTR_GET_CE_CACHE(class_name)) { + if (ZSTR_HAS_CE_CACHE(class_name) && ZSTR_GET_CE_CACHE(class_name)) { return (zend_class_entry*)ZSTR_GET_CE_CACHE(class_name); - } else { - zend_class_entry *ce = zend_hash_find_ptr(EG(class_table), lc_name); - if (ce) { - return ce; - } + } + zend_class_entry *ce = zend_hash_find_ptr(EG(class_table), lc_name); + if (ce != NULL) { + return ce; } zend_hash_move_forward_ex(spl_autoload_functions, &pos); @@ -471,14 +408,14 @@ PHP_FUNCTION(spl_autoload_call) zend_hash_rehash(ht); \ } while (0) -static Bucket *spl_find_registered_function(autoload_func_info *find_alfi) { +static Bucket *spl_find_registered_function(const zend_fcall_info_cache *find_fcc) { if (!spl_autoload_functions) { return NULL; } - autoload_func_info *alfi; - ZEND_HASH_MAP_FOREACH_PTR(spl_autoload_functions, alfi) { - if (autoload_func_info_equals(alfi, find_alfi)) { + zend_fcall_info_cache *fcc; + ZEND_HASH_MAP_FOREACH_PTR(spl_autoload_functions, fcc) { + if (zend_fcc_equals(fcc, find_fcc)) { return _p; } } ZEND_HASH_FOREACH_END(); @@ -492,7 +429,6 @@ PHP_FUNCTION(spl_autoload_register) bool prepend = 0; zend_fcall_info fci = {0}; zend_fcall_info_cache fcc; - autoload_func_info *alfi; ZEND_PARSE_PARAMETERS_START(0, 3) Z_PARAM_OPTIONAL @@ -508,14 +444,14 @@ PHP_FUNCTION(spl_autoload_register) if (!spl_autoload_functions) { ALLOC_HASHTABLE(spl_autoload_functions); - zend_hash_init(spl_autoload_functions, 1, NULL, autoload_func_info_zval_dtor, 0); + zend_hash_init(spl_autoload_functions, 1, NULL, autoload_func_info_zval_dtor, false); /* Initialize as non-packed hash table for prepend functionality. */ zend_hash_real_init_mixed(spl_autoload_functions); } /* If first arg is not null */ if (ZEND_FCI_INITIALIZED(fci)) { - if (!fcc.function_handler) { + if (!ZEND_FCC_INITIALIZED(fcc)) { /* Call trampoline has been cleared by zpp. Refetch it, because we want to deal * with it ourselves. It is important that it is not refetched on every call, * because calls may occur from different scopes. */ @@ -527,30 +463,19 @@ PHP_FUNCTION(spl_autoload_register) zend_argument_value_error(1, "must not be the spl_autoload_call() function"); RETURN_THROWS(); } - - alfi = autoload_func_info_from_fci(&fci, &fcc); - if (UNEXPECTED(alfi->func_ptr == &EG(trampoline))) { - zend_function *copy = emalloc(sizeof(zend_op_array)); - - memcpy(copy, alfi->func_ptr, sizeof(zend_op_array)); - alfi->func_ptr->common.function_name = NULL; - alfi->func_ptr = copy; - } } else { - alfi = emalloc(sizeof(autoload_func_info)); - alfi->func_ptr = zend_hash_str_find_ptr( - CG(function_table), "spl_autoload", sizeof("spl_autoload") - 1); - alfi->obj = NULL; - alfi->ce = NULL; - alfi->closure = NULL; + memset(&fcc, 0, sizeof(fcc)); + fcc.function_handler = zend_hash_str_find_ptr(CG(function_table), ZEND_STRL("spl_autoload")); } - if (spl_find_registered_function(alfi)) { - autoload_func_info_destroy(alfi); + if (spl_find_registered_function(&fcc)) { + /* Release call trampoline */ + zend_release_fcall_info_cache(&fcc); RETURN_TRUE; } - zend_hash_next_index_insert_ptr(spl_autoload_functions, alfi); + zend_fcc_addref(&fcc); + zend_hash_next_index_insert_mem(spl_autoload_functions, &fcc, sizeof(zend_fcall_info_cache)); if (prepend && spl_autoload_functions->nNumOfElements > 1) { /* Move the newly created element to the head of the hashtable */ HT_MOVE_TAIL_TO_HEAD(spl_autoload_functions); @@ -592,9 +517,9 @@ PHP_FUNCTION(spl_autoload_unregister) zend_is_callable_ex(&fci.function_name, NULL, 0, NULL, &fcc, NULL); } - autoload_func_info *alfi = autoload_func_info_from_fci(&fci, &fcc); - Bucket *p = spl_find_registered_function(alfi); - autoload_func_info_destroy(alfi); + Bucket *p = spl_find_registered_function(&fcc); + /* Release trampoline */ + zend_release_fcall_info_cache(&fcc); if (p) { zend_hash_del_bucket(spl_autoload_functions, p); RETURN_TRUE; @@ -606,32 +531,19 @@ PHP_FUNCTION(spl_autoload_unregister) /* {{{ Return all registered autoloader functions */ PHP_FUNCTION(spl_autoload_functions) { - autoload_func_info *alfi; - ZEND_PARSE_PARAMETERS_NONE(); - array_init(return_value); if (spl_autoload_functions) { - ZEND_HASH_MAP_FOREACH_PTR(spl_autoload_functions, alfi) { - if (alfi->closure) { - GC_ADDREF(alfi->closure); - add_next_index_object(return_value, alfi->closure); - } else if (alfi->func_ptr->common.scope) { - zval tmp; - - array_init(&tmp); - if (alfi->obj) { - GC_ADDREF(alfi->obj); - add_next_index_object(&tmp, alfi->obj); - } else { - add_next_index_str(&tmp, zend_string_copy(alfi->ce->name)); - } - add_next_index_str(&tmp, zend_string_copy(alfi->func_ptr->common.function_name)); - add_next_index_zval(return_value, &tmp); - } else { - add_next_index_str(return_value, zend_string_copy(alfi->func_ptr->common.function_name)); - } + zend_fcall_info_cache *fcc; + + array_init_size(return_value, zend_hash_num_elements(spl_autoload_functions)); + ZEND_HASH_MAP_FOREACH_PTR(spl_autoload_functions, fcc) { + zval tmp; + zend_get_callable_zval_from_fcc(fcc, &tmp); + add_next_index_zval(return_value, &tmp); } ZEND_HASH_FOREACH_END(); + } else { + RETURN_EMPTY_ARRAY(); } } /* }}} */ From ab2cd2d6b6947923b3d75e0f5c7ef55d9a44a0dc Mon Sep 17 00:00:00 2001 From: Gina Peter Banyard Date: Mon, 5 Jan 2026 18:43:11 +0100 Subject: [PATCH 4/9] ext/spl: inline HT_MOVE_TAIL_TO_HEAD macro This is used only once anyway --- ext/spl/php_spl.c | 16 +++++----------- 1 file changed, 5 insertions(+), 11 deletions(-) diff --git a/ext/spl/php_spl.c b/ext/spl/php_spl.c index bfe8e6f29017f..873f1e3a83ec0 100644 --- a/ext/spl/php_spl.c +++ b/ext/spl/php_spl.c @@ -398,16 +398,6 @@ PHP_FUNCTION(spl_autoload_call) zend_string_release(lc_name); } /* }}} */ -#define HT_MOVE_TAIL_TO_HEAD(ht) \ - ZEND_ASSERT(!HT_IS_PACKED(ht)); \ - do { \ - Bucket tmp = (ht)->arData[(ht)->nNumUsed-1]; \ - memmove((ht)->arData + 1, (ht)->arData, \ - sizeof(Bucket) * ((ht)->nNumUsed - 1)); \ - (ht)->arData[0] = tmp; \ - zend_hash_rehash(ht); \ - } while (0) - static Bucket *spl_find_registered_function(const zend_fcall_info_cache *find_fcc) { if (!spl_autoload_functions) { return NULL; @@ -478,7 +468,11 @@ PHP_FUNCTION(spl_autoload_register) zend_hash_next_index_insert_mem(spl_autoload_functions, &fcc, sizeof(zend_fcall_info_cache)); if (prepend && spl_autoload_functions->nNumOfElements > 1) { /* Move the newly created element to the head of the hashtable */ - HT_MOVE_TAIL_TO_HEAD(spl_autoload_functions); + ZEND_ASSERT(!HT_IS_PACKED(spl_autoload_functions)); + Bucket tmp = spl_autoload_functions->arData[spl_autoload_functions->nNumUsed-1]; + memmove(spl_autoload_functions->arData + 1, spl_autoload_functions->arData, sizeof(Bucket) * (spl_autoload_functions->nNumUsed - 1)); + spl_autoload_functions->arData[0] = tmp; + zend_hash_rehash(spl_autoload_functions); } RETURN_TRUE; From 0f4fd2d03c301c3bac1646cf03355f5054cf0b30 Mon Sep 17 00:00:00 2001 From: Gina Peter Banyard Date: Sun, 11 Jan 2026 19:57:07 +0000 Subject: [PATCH 5/9] ext/spl: use ZPP specifier that doesn't free trampoline for spl_autoload_unregister() --- ext/spl/php_spl.c | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/ext/spl/php_spl.c b/ext/spl/php_spl.c index 873f1e3a83ec0..35cc7d426911d 100644 --- a/ext/spl/php_spl.c +++ b/ext/spl/php_spl.c @@ -485,11 +485,12 @@ PHP_FUNCTION(spl_autoload_unregister) zend_fcall_info_cache fcc; ZEND_PARSE_PARAMETERS_START(1, 1) - Z_PARAM_FUNC(fci, fcc) + Z_PARAM_FUNC_NO_TRAMPOLINE_FREE(fci, fcc) ZEND_PARSE_PARAMETERS_END(); - if (fcc.function_handler && zend_string_equals_literal( - fcc.function_handler->common.function_name, "spl_autoload_call")) { + if (zend_string_equals_literal(fcc.function_handler->common.function_name, "spl_autoload_call")) { + /* Release trampoline */ + zend_release_fcall_info_cache(&fcc); php_error_docref(NULL, E_DEPRECATED, "Using spl_autoload_call() as a callback for spl_autoload_unregister() is deprecated," " to remove all registered autoloaders, call spl_autoload_unregister()" @@ -504,13 +505,6 @@ PHP_FUNCTION(spl_autoload_unregister) RETURN_TRUE; } - if (!fcc.function_handler) { - /* Call trampoline has been cleared by zpp. Refetch it, because we want to deal - * with it ourselves. It is important that it is not refetched on every call, - * because calls may occur from different scopes. */ - zend_is_callable_ex(&fci.function_name, NULL, 0, NULL, &fcc, NULL); - } - Bucket *p = spl_find_registered_function(&fcc); /* Release trampoline */ zend_release_fcall_info_cache(&fcc); From 44d6417bd7c12ae2e7fadd7cf950529743e5bb42 Mon Sep 17 00:00:00 2001 From: David Carlier Date: Wed, 19 Nov 2025 21:19:33 +0000 Subject: [PATCH 6/9] ext/sockets: GH-20532 socket_addrinfo_lookup() sets EAI error code on resolution failures with a new optional argument. close GH-20534 --- NEWS | 2 + UPGRADING | 22 ++++++ ext/sockets/sockets.c | 11 ++- ext/sockets/sockets.stub.php | 133 ++++++++++++++++++++++++++++++++- ext/sockets/sockets_arginfo.h | 57 +++++++++++++- ext/sockets/tests/gh20532.phpt | 16 ++++ 6 files changed, 236 insertions(+), 5 deletions(-) create mode 100644 ext/sockets/tests/gh20532.phpt diff --git a/NEWS b/NEWS index 74816c94a2906..86a2174a986f9 100644 --- a/NEWS +++ b/NEWS @@ -86,6 +86,8 @@ PHP NEWS transmitted data can remain unacknowledged. (James Lucas) . Added AF_UNSPEC support for sock_addrinfo_lookup() as a sole umbrella for AF_INET* family only. (David Carlier) + . Fixed GH-20532 (socket_addrinfo_lookup gives the error code with a new optional + parameter). (David Carlier) - SPL: . DirectoryIterator key can now work better with filesystem supporting larger diff --git a/UPGRADING b/UPGRADING index 3d0a56756cdcc..b72a63796647b 100644 --- a/UPGRADING +++ b/UPGRADING @@ -77,6 +77,11 @@ PHP 8.6 UPGRADE NOTES - Phar: . Phar::mungServer() now supports reference values. +- Sockets: + . socket_addrinfo_lookup() now has an additional optional argument $error + when not null, and on failure, gives the error code (one of the EAI_* + constants). + - Zip: . ZipArchive::extractTo now raises a TypeError for the files argument if one or more of the entries is not @@ -112,6 +117,23 @@ PHP 8.6 UPGRADE NOTES - Sockets: . TCP_USER_TIMEOUT (Linux only). . AF_UNSPEC. + . EAI_BADFLAGS. + . EAI_NONAME. + . EAI_AGAIN. + . EAI_FAIL. + . EAI_NODATA. + . EAI_FAMILY. + . EAI_SOCKTYPE. + . EAI_SERVICE. + . EAI_ADDRFAMILY. + . EAI_SYSTEM. + . EAI_OVERFLOW + . EAI_INPROGRESS. + . EAI_CANCELED. + . EAI_NOTCANCELED. + . EAI_ALLDONE. + . EAI_INTR. + . EAI_IDN_ENCODE. ======================================== 11. Changes to INI File Handling diff --git a/ext/sockets/sockets.c b/ext/sockets/sockets.c index 54f862d55366f..accaf4bbbcfdc 100644 --- a/ext/sockets/sockets.c +++ b/ext/sockets/sockets.c @@ -2751,16 +2751,18 @@ PHP_FUNCTION(socket_addrinfo_lookup) { zend_string *service = NULL; zend_string *hostname, *key; - zval *hint, *zhints = NULL; + zval *hint, *zhints = NULL, *error_code = NULL; + int ret = 0; struct addrinfo hints, *result, *rp; php_addrinfo *res; - ZEND_PARSE_PARAMETERS_START(1, 3) + ZEND_PARSE_PARAMETERS_START(1, 4) Z_PARAM_STR(hostname) Z_PARAM_OPTIONAL Z_PARAM_STR_OR_NULL(service) Z_PARAM_ARRAY(zhints) + Z_PARAM_ZVAL_OR_NULL(error_code) ZEND_PARSE_PARAMETERS_END(); memset(&hints, 0, sizeof(hints)); @@ -2848,7 +2850,10 @@ PHP_FUNCTION(socket_addrinfo_lookup) } ZEND_HASH_FOREACH_END(); } - if (getaddrinfo(ZSTR_VAL(hostname), service ? ZSTR_VAL(service) : NULL, &hints, &result) != 0) { + if ((ret = getaddrinfo(ZSTR_VAL(hostname), service ? ZSTR_VAL(service) : NULL, &hints, &result)) != 0) { + if (error_code) { + ZEND_TRY_ASSIGN_REF_LONG(error_code, ret); + } RETURN_FALSE; } diff --git a/ext/sockets/sockets.stub.php b/ext/sockets/sockets.stub.php index 0f645da25ce59..56b2ac07e868f 100644 --- a/ext/sockets/sockets.stub.php +++ b/ext/sockets/sockets.stub.php @@ -2067,6 +2067,136 @@ const SHUT_RDWR = UNKNOWN; #endif + +#ifdef EAI_BADFLAGS +/** + * @var int + * @cvalue EAI_BADFLAGS + */ +const EAI_BADFLAGS = UNKNOWN; +#endif +#ifdef EAI_NONAME +/** + * @var int + * @cvalue EAI_NONAME + */ +const EAI_NONAME = UNKNOWN; +#endif +#ifdef EAI_AGAIN +/** + * @var int + * @cvalue EAI_AGAIN + */ +const EAI_AGAIN = UNKNOWN; +#endif +#ifdef EAI_FAIL +/** + * @var int + * @cvalue EAI_FAIL + */ +const EAI_FAIL = UNKNOWN; +#endif +#ifdef EAI_NODATA +/** + * @var int + * @cvalue EAI_NODATA + */ +const EAI_NODATA = UNKNOWN; +#endif +#ifdef EAI_FAMILY +/** + * @var int + * @cvalue EAI_FAMILY + */ +const EAI_FAMILY = UNKNOWN; +#endif +#ifdef EAI_SOCKTYPE +/** + * @var int + * @cvalue EAI_SOCKTYPE + */ +const EAI_SOCKTYPE = UNKNOWN; +#endif +#ifdef EAI_SERVICE +/** + * @var int + * @cvalue EAI_SERVICE + */ +const EAI_SERVICE = UNKNOWN; +#endif +#ifdef EAI_ADDRFAMILY +/** + * @var int + * @cvalue EAI_ADDRFAMILY + */ +const EAI_ADDRFAMILY = UNKNOWN; +#else +#ifdef EAI_FAMILY +/** + * @var int + * @cvalue EAI_FAMILY + */ +const EAI_ADDRFAMILY = UNKNOWN; +#else +#endif +#endif +#ifdef EAI_SYSTEM +/** + * @var int + * @cvalue EAI_SYSTEM + */ +const EAI_SYSTEM = UNKNOWN; +#endif +#ifdef EAI_OVERFLOW +/** + * @var int + * @cvalue EAI_OVERFLOW + */ +const EAI_OVERFLOW = UNKNOWN; +#endif +#ifdef EAI_INPROGRESS +/** + * @var int + * @cvalue EAI_INPROGRESS + */ +const EAI_INPROGRESS = UNKNOWN; +#endif +#ifdef EAI_CANCELED +/** + * @var int + * @cvalue EAI_CANCELED + */ +const EAI_CANCELED = UNKNOWN; +#endif +#ifdef EAI_NOTCANCELED +/** + * @var int + * @cvalue EAI_NOTCANCELED + */ +const EAI_NOTCANCELED = UNKNOWN; +#endif +#ifdef EAI_ALLDONE +/** + * @var int + * @cvalue EAI_ALLDONE + */ +const EAI_ALLDONE = UNKNOWN; +#endif +#ifdef EAI_INTR +/** + * @var int + * @cvalue EAI_INTR + */ +const EAI_INTR = UNKNOWN; +#endif +#ifdef EAI_IDN_ENCODE +/** + * @var int + * @cvalue EAI_IDN_ENCODE + */ +const EAI_IDN_ENCODE = UNKNOWN; +#endif + /** * @strict-properties * @not-serializable @@ -2187,9 +2317,10 @@ function socket_cmsg_space(int $level, int $type, int $num = 0): ?int {} /** * @return array|false + * @param int $error_code * @refcount 1 */ -function socket_addrinfo_lookup(string $host, ?string $service = null, array $hints = []): array|false {} +function socket_addrinfo_lookup(string $host, ?string $service = null, array $hints = [], &$error_code = null): array|false {} function socket_addrinfo_connect(AddressInfo $address): Socket|false {} diff --git a/ext/sockets/sockets_arginfo.h b/ext/sockets/sockets_arginfo.h index 50a81c5ee3a74..522f356f853b2 100644 --- a/ext/sockets/sockets_arginfo.h +++ b/ext/sockets/sockets_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: a89c4e54ab913728e10baf8f32b45323495d685b */ + * Stub hash: 5e71ef16f2121bd6c75794673d0e0a394759ff8b */ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_socket_select, 0, 4, MAY_BE_LONG|MAY_BE_FALSE) ZEND_ARG_TYPE_INFO(1, read, IS_ARRAY, 1) @@ -184,6 +184,7 @@ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_socket_addrinfo_lookup, 0, 1, MA ZEND_ARG_TYPE_INFO(0, host, IS_STRING, 0) ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, service, IS_STRING, 1, "null") ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, hints, IS_ARRAY, 0, "[]") + ZEND_ARG_INFO_WITH_DEFAULT_VALUE(1, error_code, "null") ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_socket_addrinfo_connect, 0, 1, Socket, MAY_BE_FALSE) @@ -1054,6 +1055,60 @@ static void register_sockets_symbols(int module_number) REGISTER_LONG_CONSTANT("SHUT_WR", SHUT_WR, CONST_PERSISTENT); REGISTER_LONG_CONSTANT("SHUT_RDWR", SHUT_RDWR, CONST_PERSISTENT); #endif +#if defined(EAI_BADFLAGS) + REGISTER_LONG_CONSTANT("EAI_BADFLAGS", EAI_BADFLAGS, CONST_PERSISTENT); +#endif +#if defined(EAI_NONAME) + REGISTER_LONG_CONSTANT("EAI_NONAME", EAI_NONAME, CONST_PERSISTENT); +#endif +#if defined(EAI_AGAIN) + REGISTER_LONG_CONSTANT("EAI_AGAIN", EAI_AGAIN, CONST_PERSISTENT); +#endif +#if defined(EAI_FAIL) + REGISTER_LONG_CONSTANT("EAI_FAIL", EAI_FAIL, CONST_PERSISTENT); +#endif +#if defined(EAI_NODATA) + REGISTER_LONG_CONSTANT("EAI_NODATA", EAI_NODATA, CONST_PERSISTENT); +#endif +#if defined(EAI_FAMILY) + REGISTER_LONG_CONSTANT("EAI_FAMILY", EAI_FAMILY, CONST_PERSISTENT); +#endif +#if defined(EAI_SOCKTYPE) + REGISTER_LONG_CONSTANT("EAI_SOCKTYPE", EAI_SOCKTYPE, CONST_PERSISTENT); +#endif +#if defined(EAI_SERVICE) + REGISTER_LONG_CONSTANT("EAI_SERVICE", EAI_SERVICE, CONST_PERSISTENT); +#endif +#if defined(EAI_ADDRFAMILY) + REGISTER_LONG_CONSTANT("EAI_ADDRFAMILY", EAI_ADDRFAMILY, CONST_PERSISTENT); +#endif +#if !(defined(EAI_ADDRFAMILY)) && defined(EAI_FAMILY) + REGISTER_LONG_CONSTANT("EAI_ADDRFAMILY", EAI_FAMILY, CONST_PERSISTENT); +#endif +#if defined(EAI_SYSTEM) + REGISTER_LONG_CONSTANT("EAI_SYSTEM", EAI_SYSTEM, CONST_PERSISTENT); +#endif +#if defined(EAI_OVERFLOW) + REGISTER_LONG_CONSTANT("EAI_OVERFLOW", EAI_OVERFLOW, CONST_PERSISTENT); +#endif +#if defined(EAI_INPROGRESS) + REGISTER_LONG_CONSTANT("EAI_INPROGRESS", EAI_INPROGRESS, CONST_PERSISTENT); +#endif +#if defined(EAI_CANCELED) + REGISTER_LONG_CONSTANT("EAI_CANCELED", EAI_CANCELED, CONST_PERSISTENT); +#endif +#if defined(EAI_NOTCANCELED) + REGISTER_LONG_CONSTANT("EAI_NOTCANCELED", EAI_NOTCANCELED, CONST_PERSISTENT); +#endif +#if defined(EAI_ALLDONE) + REGISTER_LONG_CONSTANT("EAI_ALLDONE", EAI_ALLDONE, CONST_PERSISTENT); +#endif +#if defined(EAI_INTR) + REGISTER_LONG_CONSTANT("EAI_INTR", EAI_INTR, CONST_PERSISTENT); +#endif +#if defined(EAI_IDN_ENCODE) + REGISTER_LONG_CONSTANT("EAI_IDN_ENCODE", EAI_IDN_ENCODE, CONST_PERSISTENT); +#endif } static zend_class_entry *register_class_Socket(void) diff --git a/ext/sockets/tests/gh20532.phpt b/ext/sockets/tests/gh20532.phpt new file mode 100644 index 0000000000000..f3368c830369a --- /dev/null +++ b/ext/sockets/tests/gh20532.phpt @@ -0,0 +1,16 @@ +--TEST-- +GH-20562 - socket_addrinfo_lookup() returns error codes on resolution failures. +--EXTENSIONS-- +sockets +--FILE-- + AF_INET], $error_code) === false && in_array($error_code, [EAI_FAMILY, EAI_ADDRFAMILY, EAI_NONAME, EAI_NODATA])); +var_dump(socket_addrinfo_lookup("example.com", "http", ['ai_socktype' => SOCK_RAW, 'ai_flags' => 2147483647], $error_code) === false && in_array($error_code, [EAI_SOCKTYPE, EAI_SERVICE, EAI_BADFLAGS, EAI_NONAME])); +?> +--EXPECT-- +bool(true) +bool(true) +bool(true) + From 8f55b0850fd3ab030fffff3d3764d58d06ea744a Mon Sep 17 00:00:00 2001 From: David CARLIER Date: Tue, 20 Jan 2026 21:34:50 +0000 Subject: [PATCH 7/9] ext/sqlite3: Sqlite3::openBlob() code path simplification. (#20969) * ext/sqlite3: Sqlite3::openBlob() code path simplification. since the stream is opened in non persistent mode, the failure code path is dead (so are the missing leaks fixes). --- ext/sqlite3/sqlite3.c | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/ext/sqlite3/sqlite3.c b/ext/sqlite3/sqlite3.c index da24b037861a8..6d55409ca45aa 100644 --- a/ext/sqlite3/sqlite3.c +++ b/ext/sqlite3/sqlite3.c @@ -1270,13 +1270,10 @@ PHP_METHOD(SQLite3, openBlob) mode = "r+b"; } + // since it is not persistent, php_stream_alloc can't fail stream = php_stream_alloc(&php_stream_sqlite3_ops, sqlite3_stream, 0, mode); - - if (stream) { - php_stream_to_zval(stream, return_value); - } else { - RETURN_FALSE; - } + ZEND_ASSERT(stream != NULL); + php_stream_to_zval(stream, return_value); } /* }}} */ From 724ff603181e2b4e4c2c06851369a7968579413b Mon Sep 17 00:00:00 2001 From: Kamil Tekiela Date: Tue, 20 Jan 2026 22:33:02 +0000 Subject: [PATCH 8/9] Add MYSQLI_OPT_COMPRESS (#20987) --- UPGRADING | 3 +++ ext/mysqli/mysqli.stub.php | 5 +++++ ext/mysqli/mysqli_arginfo.h | 3 ++- ext/mysqli/tests/mysqli_constants.phpt | 1 + 4 files changed, 11 insertions(+), 1 deletion(-) diff --git a/UPGRADING b/UPGRADING index b72a63796647b..0d24268b787a9 100644 --- a/UPGRADING +++ b/UPGRADING @@ -110,6 +110,9 @@ PHP 8.6 UPGRADE NOTES - Hash: . The bundled version of xxHash was upgraded to 0.8.2. +- mysqli + . Added new constant MYSQLI_OPT_COMPRESS. + ======================================== 10. New Global Constants ======================================== diff --git a/ext/mysqli/mysqli.stub.php b/ext/mysqli/mysqli.stub.php index 370da77e62a25..7ca2a20ca1b77 100644 --- a/ext/mysqli/mysqli.stub.php +++ b/ext/mysqli/mysqli.stub.php @@ -58,6 +58,11 @@ * @cvalue MYSQL_OPT_SSL_VERIFY_SERVER_CERT */ const MYSQLI_OPT_SSL_VERIFY_SERVER_CERT = UNKNOWN; +/** + * @var int + * @cvalue MYSQL_OPT_COMPRESS + */ +const MYSQLI_OPT_COMPRESS = UNKNOWN; /** * @var int diff --git a/ext/mysqli/mysqli_arginfo.h b/ext/mysqli/mysqli_arginfo.h index 789464a762538..717af442d84fc 100644 --- a/ext/mysqli/mysqli_arginfo.h +++ b/ext/mysqli/mysqli_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: fecde55745fb219cb15fd35a54a71371ef2b8b7d */ + * Stub hash: c0af8c627a063fbd2c8b76c63b5e70c8ddd1e4f9 */ 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) @@ -1045,6 +1045,7 @@ static void register_mysqli_symbols(int module_number) REGISTER_LONG_CONSTANT("MYSQLI_OPT_NET_READ_BUFFER_SIZE", MYSQLND_OPT_NET_READ_BUFFER_SIZE, CONST_PERSISTENT); REGISTER_LONG_CONSTANT("MYSQLI_OPT_INT_AND_FLOAT_NATIVE", MYSQLND_OPT_INT_AND_FLOAT_NATIVE, CONST_PERSISTENT); REGISTER_LONG_CONSTANT("MYSQLI_OPT_SSL_VERIFY_SERVER_CERT", MYSQL_OPT_SSL_VERIFY_SERVER_CERT, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("MYSQLI_OPT_COMPRESS", MYSQL_OPT_COMPRESS, CONST_PERSISTENT); REGISTER_LONG_CONSTANT("MYSQLI_SERVER_PUBLIC_KEY", MYSQL_SERVER_PUBLIC_KEY, CONST_PERSISTENT); REGISTER_LONG_CONSTANT("MYSQLI_CLIENT_SSL", CLIENT_SSL, CONST_PERSISTENT); REGISTER_LONG_CONSTANT("MYSQLI_CLIENT_COMPRESS", CLIENT_COMPRESS, CONST_PERSISTENT); diff --git a/ext/mysqli/tests/mysqli_constants.phpt b/ext/mysqli/tests/mysqli_constants.phpt index 982fe47f75cef..0b5fa977ac1dc 100644 --- a/ext/mysqli/tests/mysqli_constants.phpt +++ b/ext/mysqli/tests/mysqli_constants.phpt @@ -47,6 +47,7 @@ $expected_constants = array( "MYSQLI_SERVER_QUERY_NO_GOOD_INDEX_USED"=> true, "MYSQLI_SERVER_QUERY_NO_INDEX_USED" => true, "MYSQLI_OPT_LOAD_DATA_LOCAL_DIR" => true, + "MYSQLI_OPT_COMPRESS" => true, "MYSQLI_IS_MARIADB" => true, "MYSQLI_TYPE_DECIMAL" => true, From 1db1c7f5c1b4148e8de2e403ed6a582e3167c758 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Chris=20Hasi=C5=84ski?= Date: Sat, 17 Jan 2026 00:25:44 +0100 Subject: [PATCH 9/9] Fix segfault in Tracing JIT with object reference (GH-20818) When FE_RESET_RW executes, it converts the CV to a reference before checking if the array/object is empty. However, when the JIT creates exit points for FE_RESET_RW in zend_jit_trace_handler(), it wasn't updating the stack type for op1 to reflect this change. This caused side traces compiled from these exit points to have incorrect type information. The side trace's CV cleanup code would see IS_OBJECT and generate a direct call to zend_objects_store_del(), but the actual value was a zend_reference*, causing a segfault. The fix adds ZEND_FE_RESET_RW to the list of opcodes that temporarily set their op1 stack type to IS_UNKNOWN before creating exit points. This follows the same pattern used for ZEND_BIND_INIT_STATIC_OR_JMP. When IS_UNKNOWN, the JIT falls back to SSA type info which correctly includes MAY_BE_REF for FE_RESET_RW's op1_def. Fixes GH-20818 Closes GH-20948 --- NEWS | 4 ++++ ext/opcache/jit/zend_jit_ir.c | 2 ++ ext/opcache/tests/jit/gh20818.phpt | 30 ++++++++++++++++++++++++++++++ 3 files changed, 36 insertions(+) create mode 100644 ext/opcache/tests/jit/gh20818.phpt diff --git a/NEWS b/NEWS index 80b69c11cf88d..7d90a01f51d4b 100644 --- a/NEWS +++ b/NEWS @@ -25,6 +25,10 @@ PHP NEWS . Fixed bug GH-20836 (Stack overflow in mb_convert_variables with recursive array references). (alexandre-daubois) +- Opcache: + . Fixed bug GH-20818 (Segfault in Tracing JIT with object reference). + (khasinski) + - Phar: . Fixed bug GH-20882 (buildFromIterator breaks with missing base directory). (ndossche) diff --git a/ext/opcache/jit/zend_jit_ir.c b/ext/opcache/jit/zend_jit_ir.c index 4f25678e11439..3ddaa0270881e 100644 --- a/ext/opcache/jit/zend_jit_ir.c +++ b/ext/opcache/jit/zend_jit_ir.c @@ -16938,6 +16938,7 @@ static int zend_jit_trace_handler(zend_jit_ctx *jit, const zend_op_array *op_arr SET_STACK_TYPE(stack, EX_VAR_TO_NUM(opline->op2.var), IS_UNKNOWN, 1); } break; + case ZEND_FE_RESET_RW: case ZEND_BIND_INIT_STATIC_OR_JMP: if (opline->op1_type == IS_CV) { old_info = STACK_INFO(stack, EX_VAR_TO_NUM(opline->op1.var)); @@ -16962,6 +16963,7 @@ static int zend_jit_trace_handler(zend_jit_ctx *jit, const zend_op_array *op_arr SET_STACK_INFO(stack, EX_VAR_TO_NUM(opline->op2.var), old_info); } break; + case ZEND_FE_RESET_RW: case ZEND_BIND_INIT_STATIC_OR_JMP: if (opline->op1_type == IS_CV) { SET_STACK_INFO(stack, EX_VAR_TO_NUM(opline->op1.var), old_info); diff --git a/ext/opcache/tests/jit/gh20818.phpt b/ext/opcache/tests/jit/gh20818.phpt new file mode 100644 index 0000000000000..9423856b66bec --- /dev/null +++ b/ext/opcache/tests/jit/gh20818.phpt @@ -0,0 +1,30 @@ +--TEST-- +GH-20818 (Segfault in Tracing JIT with Object Reference) +--INI-- +opcache.enable=1 +opcache.enable_cli=1 +opcache.jit=tracing +opcache.jit_buffer_size=1M +--FILE-- + 1], + (object) ["" => 1], + (object) [], +]; + +for ($i = 0; $i < 200; $i += 1) { + foreach ($data as $entry) { + process($entry); + } +} + +echo "Done\n"; +?> +--EXPECT-- +Done