From 340148308347f2e010d3d6cf810187d63f6eea92 Mon Sep 17 00:00:00 2001 From: Ilija Tovilo Date: Fri, 25 Sep 2026 16:58:21 +0200 Subject: [PATCH 1/6] Skip result type inference verification for simple get Simple get uses VM recursion rather than C stack recursion, which means the result is not actually written when the handler ends. Skip verification when we detect a switch to a different stack frame. --- ...et_result_type_inference_verification.phpt | 25 +++++++++++++++++++ Zend/zend_verify_type_inference.h | 3 ++- 2 files changed, 27 insertions(+), 1 deletion(-) create mode 100644 Zend/tests/property_hooks/simple_get_result_type_inference_verification.phpt diff --git a/Zend/tests/property_hooks/simple_get_result_type_inference_verification.phpt b/Zend/tests/property_hooks/simple_get_result_type_inference_verification.phpt new file mode 100644 index 000000000000..214a568e297d --- /dev/null +++ b/Zend/tests/property_hooks/simple_get_result_type_inference_verification.phpt @@ -0,0 +1,25 @@ +--TEST-- +Result type inference verification for simple get is skipped +--FILE-- + $this->backing; + } + + public function test(): void { + $prop = $this->prop; + } +} + +$foo = new Foo; +$foo->test(); +$foo->test(); +echo "Done\n"; + +?> +--EXPECT-- +Done diff --git a/Zend/zend_verify_type_inference.h b/Zend/zend_verify_type_inference.h index 0add01933495..6f4f8df43b01 100644 --- a/Zend/zend_verify_type_inference.h +++ b/Zend/zend_verify_type_inference.h @@ -187,7 +187,8 @@ static void zend_verify_inference_def(zend_execute_data *execute_data, const zen && opline->opcode != ZEND_DO_FCALL_BY_NAME /* ZEND_FE_FETCH_R[W] does not define a result in the last iteration. */ && opline->opcode != ZEND_FE_FETCH_R - && opline->opcode != ZEND_FE_FETCH_RW) { + && opline->opcode != ZEND_FE_FETCH_RW + && (opline->opcode != ZEND_FETCH_OBJ_R || EG(current_execute_data) == execute_data)) { zend_verify_type_inference(EX_VAR(opline->result.var), opline->result_def_type, opline->result_type, execute_data, opline, "result_def"); /* Verify return value in the context of caller. */ From ead145b4bae88f16c416cb9a153bdc41b1ce3e70 Mon Sep 17 00:00:00 2001 From: Ilija Tovilo Date: Fri, 25 Sep 2026 17:08:49 +0200 Subject: [PATCH 2/6] [skip ci] Fix mb_ereg_replace_kname_unterminated_nul.phpt SKIPIF mbregex is not always available. --- .../tests/mb_ereg_replace_kname_unterminated_nul.phpt | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/ext/mbstring/tests/mb_ereg_replace_kname_unterminated_nul.phpt b/ext/mbstring/tests/mb_ereg_replace_kname_unterminated_nul.phpt index 67a39dc9f95c..1783b40ab5c9 100644 --- a/ext/mbstring/tests/mb_ereg_replace_kname_unterminated_nul.phpt +++ b/ext/mbstring/tests/mb_ereg_replace_kname_unterminated_nul.phpt @@ -2,6 +2,10 @@ mb_ereg_replace() with unterminated \k backref must not embed a NUL byte --EXTENSIONS-- mbstring +--SKIPIF-- + --FILE-- Date: Fri, 25 Sep 2026 15:27:27 +0000 Subject: [PATCH 3/6] Enable C11 for MSVC builds (#23912) * Enable C11 for MSVC C sources * Respect requested C standard * Split mixed C and C++ builds --- ext/intl/config.w32 | 2 +- win32/build/confutils.js | 23 +++++++++++++++++++---- 2 files changed, 20 insertions(+), 5 deletions(-) diff --git a/ext/intl/config.w32 b/ext/intl/config.w32 index fb30953697b6..5f689214c1b6 100644 --- a/ext/intl/config.w32 +++ b/ext/intl/config.w32 @@ -9,7 +9,7 @@ if (PHP_INTL != "no") { CHECK_HEADER("unicode/utf.h", "CFLAGS_INTL")) { // always build as shared - zend_strtod.c/ICU type conflict EXTENSION("intl", "php_intl.c intl_convert.c intl_icu_compat.c intl_convertcpp.cpp intl_error.c ", true, - "/I \"" + configure_module_dirname + "\" /DZEND_ENABLE_STATIC_TSRMLS_CACHE=1"); + "/I \"" + configure_module_dirname + "\" /DZEND_ENABLE_STATIC_TSRMLS_CACHE=1", null, undefined, true); ADD_EXTENSION_DEP('intl', 'date'); ADD_SOURCES(configure_module_dirname + "/collator", "\ collator_attr.cpp \ diff --git a/win32/build/confutils.js b/win32/build/confutils.js index 7c2cab480bf5..c5128f70498e 100644 --- a/win32/build/confutils.js +++ b/win32/build/confutils.js @@ -58,6 +58,7 @@ var MINRE2C = "1.0.3"; /* Store the enabled extensions (summary + QA check) */ var extensions_enabled = new Array(); +var cxx_mode_targets = {}; /* Store the SAPI enabled (summary + QA check) */ var sapi_enabled = new Array(); @@ -1460,13 +1461,17 @@ function ZEND_EXTENSION(extname, file_list, shared, cflags, dllname, obj_dir) extensions_enabled[extensions_enabled.length - 1][2] = true; } -function EXTENSION(extname, file_list, shared, cflags, dllname, obj_dir) +function EXTENSION(extname, file_list, shared, cflags, dllname, obj_dir, cxx_mode) { var objs = null; var EXT = extname.toUpperCase(); var extname_for_printing; var ldflags; + if (cxx_mode) { + cxx_mode_targets[extname] = true; + } + if (shared == null) { if (force_all_shared()) { shared = true; @@ -1804,8 +1809,9 @@ function ADD_SOURCES(dir, file_list, target, obj_dir, duplicate_sources) var _tmp = src.split("\\"); var filename = _tmp.pop(); obj = filename.replace(re, ".obj"); + var c11_flag = VS_TOOLSET && !cxx_mode_targets[target] && /\.c$/i.test(src) ? " /std:c11" : ""; - MFO.WriteLine("\t" + CMD_MOD1 + "$(CC) $(" + flags + ") $(CFLAGS) $(" + bd_flags_name + ") /c " + dir + "\\" + src + " /Fo" + sub_build + d + obj); + MFO.WriteLine("\t" + CMD_MOD1 + "$(CC)" + c11_flag + " $(" + flags + ") $(CFLAGS) $(" + bd_flags_name + ") /c " + dir + "\\" + src + " /Fo" + sub_build + d + obj); if ("clang" == PHP_ANALYZER) { MFO.WriteLine("\t" + CMD_MOD1 + "\"$(CLANG_CL)\" " + analyzer_base_args + " $(" + flags + "_ANALYZER) $(CFLAGS_ANALYZER) $(" + bd_flags_name + "_ANALYZER) " + dir + "\\" + src); @@ -1819,11 +1825,20 @@ function ADD_SOURCES(dir, file_list, target, obj_dir, duplicate_sources) } else { /* TODO create a response file at least for the source files to work around the cmd line length limit. */ var src_line = ""; + var src_lines = ["", ""]; for (var j in srcs_by_dir[k]) { - src_line += dir + "\\" + file_list[srcs_by_dir[k][j]] + " "; + var source = file_list[srcs_by_dir[k][j]]; + var source_path = dir + "\\" + source + " "; + src_line += source_path; + src_lines[VS_TOOLSET && /\.c$/i.test(source) ? 0 : 1] += source_path; } - MFO.WriteLine("\t" + CMD_MOD1 + "$(CC) $(" + flags + ") $(CFLAGS) /Fo" + sub_build + d + " $(" + bd_flags_name + ") /c " + src_line); + for (var language = 0; language < src_lines.length; language++) { + if (src_lines[language]) { + var c11_flag = language == 0 && !cxx_mode_targets[target] ? " /std:c11" : ""; + MFO.WriteLine("\t" + CMD_MOD1 + "$(CC)" + c11_flag + " $(" + flags + ") $(CFLAGS) /Fo" + sub_build + d + " $(" + bd_flags_name + ") /c " + src_lines[language]); + } + } if ("clang" == PHP_ANALYZER) { MFO.WriteLine("\t\"$(CLANG_CL)\" " + analyzer_base_args + " $(" + flags + "_ANALYZER) $(CFLAGS_ANALYZER) $(" + bd_flags_name + "_ANALYZER) " + src_line); From 89bddd8399cf1354c7efa29b07011183d783713a Mon Sep 17 00:00:00 2001 From: Nora Dossche <7771979+ndossche@users.noreply.github.com> Date: Fri, 25 Sep 2026 17:45:47 +0200 Subject: [PATCH 4/6] Implement fast path for string accesses with long dimensions in JIT (#23767) --- ext/opcache/jit/zend_jit_ir.c | 65 +++++++++++++++++++++++++++-------- 1 file changed, 51 insertions(+), 14 deletions(-) diff --git a/ext/opcache/jit/zend_jit_ir.c b/ext/opcache/jit/zend_jit_ir.c index c7716eb96ff8..627b1f9437a2 100644 --- a/ext/opcache/jit/zend_jit_ir.c +++ b/ext/opcache/jit/zend_jit_ir.c @@ -12749,25 +12749,62 @@ static int zend_jit_fetch_dim_read(zend_jit_ctx *jit, ir_IF_TRUE(if_type); } } - jit_SET_EX_OPLINE(jit, opline); str_ref = jit_Z_PTR(jit, op1_addr); - if (opline->opcode != ZEND_FETCH_DIM_IS) { - ir_ref ref; - if ((op2_info & (MAY_BE_ANY|MAY_BE_UNDEF|MAY_BE_GUARD)) == MAY_BE_LONG) { - ref = ir_CALL_2(IR_ADDR, ir_CONST_FC_FUNC(zend_jit_fetch_dim_str_offset_r_helper), - str_ref, jit_Z_LVAL(jit, op2_addr)); - } else { - ref = ir_CALL_2(IR_ADDR, ir_CONST_FC_FUNC(zend_jit_fetch_dim_str_r_helper), - str_ref, jit_ZVAL_ADDR(jit, op2_addr)); - } + /* Inlined offset read for integer offsets into strings. */ + if (opline->opcode != ZEND_FETCH_DIM_IS + && (op1_info & (MAY_BE_ANY|MAY_BE_UNDEF)) == MAY_BE_STRING + && (op2_info & (MAY_BE_ANY|MAY_BE_UNDEF|MAY_BE_GUARD)) == MAY_BE_LONG) { + ir_ref offset_ref = jit_Z_LVAL(jit, op2_addr); + ir_ref len_ref = ir_LOAD_L(ir_ADD_OFFSET(str_ref, offsetof(zend_string, len))); + ir_ref real_offset_ref = offset_ref; + if (!op2_range || op2_range->min < 0) { + // JIT: if (offset < 0) offset += ZSTR_LEN(str); + /* Branchless way to add -1 for negative offsets to the string length. */ + real_offset_ref = ir_ADD_L(offset_ref, + ir_AND_L(len_ref, + ir_SAR_L(offset_ref, ir_CONST_LONG(SIZEOF_ZEND_LONG * 8 - 1)))); + } + + /* An offset that is still negative wraps around and fails this check as well. */ + ir_ref if_in_range = ir_IF(ir_ULT(real_offset_ref, len_ref)); + + ir_IF_TRUE(if_in_range); + // JIT: result = ZSTR_CHAR((uint8_t)ZSTR_VAL(str)[offset]); + ir_ref ref = ir_LOAD_A( + ir_ADD_A( + ir_CONST_ADDR(zend_one_char_string), + ir_MUL_A( + ir_ZEXT_A( + ir_LOAD_U8( + ir_ADD_A(ir_ADD_OFFSET(str_ref, offsetof(zend_string, val)), + ir_BITCAST_A(real_offset_ref)))), + ir_CONST_ADDR(sizeof(zend_string*))))); + ir_ref fast_path = ir_END(); + + ir_IF_FALSE_cold(if_in_range); + jit_SET_EX_OPLINE(jit, opline); + ir_ref slow_ref = ir_CALL_2(IR_ADDR, ir_CONST_FC_FUNC(zend_jit_fetch_dim_str_offset_r_helper), + str_ref, offset_ref); + + ir_MERGE_WITH(fast_path); + ref = ir_PHI_2(IR_ADDR, slow_ref, ref); jit_set_Z_PTR(jit, res_addr, ref); jit_set_Z_TYPE_INFO(jit, res_addr, IS_STRING); } else { - ir_CALL_3(IR_VOID, ir_CONST_FC_FUNC(zend_jit_fetch_dim_str_is_helper), - str_ref, - jit_ZVAL_ADDR(jit, op2_addr), - jit_ZVAL_ADDR(jit, res_addr)); + jit_SET_EX_OPLINE(jit, opline); + if (opline->opcode != ZEND_FETCH_DIM_IS) { + ir_ref ref = ir_CALL_2(IR_ADDR, ir_CONST_FC_FUNC(zend_jit_fetch_dim_str_r_helper), + str_ref, jit_ZVAL_ADDR(jit, op2_addr)); + + jit_set_Z_PTR(jit, res_addr, ref); + jit_set_Z_TYPE_INFO(jit, res_addr, IS_STRING); + } else { + ir_CALL_3(IR_VOID, ir_CONST_FC_FUNC(zend_jit_fetch_dim_str_is_helper), + str_ref, + jit_ZVAL_ADDR(jit, op2_addr), + jit_ZVAL_ADDR(jit, res_addr)); + } } ir_END_list(end_inputs); } From 32f514962aa31108220f6cc9fcae7635cf53274e Mon Sep 17 00:00:00 2001 From: Jakub Zelenka Date: Fri, 25 Sep 2026 17:45:20 +0200 Subject: [PATCH 5/6] Zend tests: skip syslog() in the arginfo/zpp mismatch test With weak typing syslog(null, null) is syslog(0, ""), and priority 0 is LOG_EMERG. journald broadcasts emergency messages to every logged in terminal, so each run of the test printed a "Broadcast message from systemd-journald" banner in all terminals. Skip it like error_log() and mail(), which the test already leaves out for their side effects. --- Zend/tests/arginfo_zpp_mismatch.inc | 1 + 1 file changed, 1 insertion(+) diff --git a/Zend/tests/arginfo_zpp_mismatch.inc b/Zend/tests/arginfo_zpp_mismatch.inc index 5b2711fdb630..0ac5df8ceffd 100644 --- a/Zend/tests/arginfo_zpp_mismatch.inc +++ b/Zend/tests/arginfo_zpp_mismatch.inc @@ -18,6 +18,7 @@ function skipFunction($function): bool { || (is_string($function) && str_starts_with($function, 'ob_')) || $function === 'output_add_rewrite_var' || $function === 'error_log' + || $function === 'syslog' /* may spend a lot of time waiting for connection timeouts */ || (is_string($function) && str_contains($function, 'connect')) || (is_string($function) && str_starts_with($function, 'snmp')) From 1c3aa68e662062d5fb06328b15d4a96e669e32b7 Mon Sep 17 00:00:00 2001 From: Weilin Du Date: Sat, 26 Sep 2026 03:37:51 +0800 Subject: [PATCH 6/6] [skip ci] Several UPGRADING fixes Use JSON_PRETTY_PRINT, the constant exposed to PHP code, instead of the internal C macro PHP_JSON_PRETTY_PRINT in the json_encode() performance note. Qualify url_percent_encode() as Uri\WhatWg\url_percent_encode() to match its declared namespace. Uri\url_percent_encode() does not exist. --- UPGRADING | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/UPGRADING b/UPGRADING index e390a70753b5..63a1f5f30fbc 100644 --- a/UPGRADING +++ b/UPGRADING @@ -525,7 +525,7 @@ PHP 8.6 UPGRADE NOTES RFC: https://wiki.php.net/rfc/uri_followup#host_type_detection . Added Uri\Rfc3986\UriBuilder and Uri\WhatWg\UrlBuilder. RFC: https://wiki.php.net/rfc/uri_followup#uri_building - . Added Uri\url_percent_encode(). + . Added Uri\WhatWg\url_percent_encode(). RFC: https://wiki.php.net/rfc/uri_followup#percent-encoding_support ======================================== @@ -1132,7 +1132,7 @@ PHP 8.6 UPGRADE NOTES - JSON: . Improve performance of encoding arrays and objects. . Improved performance of indentation generation in json_encode() - when using PHP_JSON_PRETTY_PRINT. + when using JSON_PRETTY_PRINT. - Mbstring: . Improved performance of mb_str_pad().