From 6a17febe6c92119f079969e6b3ab09d03686e65e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim=20D=C3=BCsterhus?= Date: Mon, 26 Jan 2026 08:59:30 +0100 Subject: [PATCH 1/2] zend_compile: Fix array_map() optimization (#21016) * zend_compile: Fix handling of PFA syntax in array_map() optimization PFA is not implemented and the syntax is rejected at compile-time, thus it was assumed the assertion would be unreachable. However the check for PFA syntax happens after compiling special functions, making it reachable. Fix this by gracefully returning FAILURE which will then correctly emit the error during the compilation of the normal call. Fixes php/php-src#20991. * zend_compile: Fix array_map() optimization for dynamic function names Fixes php/php-src#20990. * zend_compile: Adjust array_map() optimization after review feedback --- Zend/zend_compile.c | 24 +++--- .../array_map_foreach_optimization_006.phpt | 84 +++++++++++++++++++ 2 files changed, 95 insertions(+), 13 deletions(-) create mode 100644 ext/opcache/tests/array_map_foreach_optimization_006.phpt diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c index 916d8eebd896..1ce921d4fec2 100644 --- a/Zend/zend_compile.c +++ b/Zend/zend_compile.c @@ -5054,25 +5054,23 @@ static zend_result zend_compile_func_array_map(znode *result, zend_ast_list *arg /* Bail out if the callback is assert() due to the AST stringification logic * breaking for the generated call. */ - if (callback->kind == ZEND_AST_CALL && zend_string_equals_literal_ci(zend_ast_get_str(callback->child[0]), "assert")) { + if (callback->kind == ZEND_AST_CALL + && callback->child[0]->kind == ZEND_AST_ZVAL + && Z_TYPE_P(zend_ast_get_zval(callback->child[0])) == IS_STRING + && zend_string_equals_literal_ci(zend_ast_get_str(callback->child[0]), "assert")) { + return FAILURE; + } + + zend_ast_list *callback_args = zend_ast_get_list(((zend_ast_fcc*)args_ast)->args); + if (callback_args->children != 1 || callback_args->child[0]->attr != ZEND_PLACEHOLDER_VARIADIC) { + /* Full PFA is not yet implemented, will fail in zend_compile_call_common(). */ return FAILURE; } znode value; value.op_type = IS_TMP_VAR; value.u.op.var = get_temporary_variable(); - - zend_ast_list *callback_args = zend_ast_get_list(((zend_ast_fcc*)args_ast)->args); - zend_ast *call_args = zend_ast_create_list(0, ZEND_AST_ARG_LIST); - for (uint32_t i = 0; i < callback_args->children; i++) { - zend_ast *child = callback_args->child[i]; - if (child->kind == ZEND_AST_PLACEHOLDER_ARG) { - call_args = zend_ast_list_add(call_args, zend_ast_create_znode(&value)); - } else { - ZEND_ASSERT(0 && "not implemented"); - call_args = zend_ast_list_add(call_args, child); - } - } + zend_ast *call_args = zend_ast_create_list(1, ZEND_AST_ARG_LIST, zend_ast_create_znode(&value)); zend_op *opline; diff --git a/ext/opcache/tests/array_map_foreach_optimization_006.phpt b/ext/opcache/tests/array_map_foreach_optimization_006.phpt new file mode 100644 index 000000000000..a3b1c534e341 --- /dev/null +++ b/ext/opcache/tests/array_map_foreach_optimization_006.phpt @@ -0,0 +1,84 @@ +--TEST-- +array_map(): foreach optimization - dynamic name +--EXTENSIONS-- +opcache +--INI-- +opcache.enable=1 +opcache.enable_cli=1 +opcache.opt_debug_level=0x20000 +--FILE-- + +--EXPECTF-- +$_main: + ; (lines=%d, args=0, vars=%d, tmps=%d) + ; (after optimizer) + ; %s +0000 INIT_FCALL 2 %d string("range") +0001 SEND_VAL int(1) 1 +0002 SEND_VAL int(10) 2 +0003 V3 = DO_ICALL +0004 ASSIGN CV0($array) V3 +0005 ASSIGN CV1($plus1) string("plus1") +0006 TYPE_ASSERT 131079 string("array_map") CV0($array) +0007 T3 = INIT_ARRAY 0 (packed) NEXT +0008 V4 = FE_RESET_R CV0($array) 0015 +0009 T6 = FE_FETCH_R V4 T5 0015 +0010 INIT_DYNAMIC_CALL 1 CV1($plus1) +0011 SEND_VAL_EX T5 1 +0012 V5 = DO_FCALL +0013 T3 = ADD_ARRAY_ELEMENT V5 T6 +0014 JMP 0009 +0015 FE_FREE V4 +0016 ASSIGN CV2($foo) T3 +0017 INIT_FCALL 1 %d string("var_dump") +0018 SEND_VAR CV2($foo) 1 +0019 DO_ICALL +0020 RETURN int(1) +LIVE RANGES: + 3: 0008 - 0016 (tmp/var) + 4: 0009 - 0015 (loop) + 5: 0010 - 0011 (tmp/var) + 6: 0010 - 0013 (tmp/var) + +plus1: + ; (lines=3, args=1, vars=1, tmps=%d) + ; (after optimizer) + ; %s +0000 CV0($x) = RECV 1 +0001 T1 = ADD CV0($x) int(1) +0002 RETURN T1 +array(10) { + [0]=> + int(2) + [1]=> + int(3) + [2]=> + int(4) + [3]=> + int(5) + [4]=> + int(6) + [5]=> + int(7) + [6]=> + int(8) + [7]=> + int(9) + [8]=> + int(10) + [9]=> + int(11) +} From d9ccf782a8ca4e6a9bf9acc5dafff91eaed53fb0 Mon Sep 17 00:00:00 2001 From: Daniel Scherzer Date: Mon, 26 Jan 2026 00:33:04 -0800 Subject: [PATCH 2/2] [8.5] NEWS: add some missing RFC entries (#20693) --- NEWS | 3 +++ 1 file changed, 3 insertions(+) diff --git a/NEWS b/NEWS index fbe9286c1ca1..9d4501107345 100644 --- a/NEWS +++ b/NEWS @@ -365,6 +365,8 @@ PHP NEWS non-printable characters in string literals). (nielsdos, WangYihang) . Fixed bug GH-17442 (Engine UAF with reference assign and dtor). (nielsdos) . Do not use RTLD_DEEPBIND if dlmopen is available. (Daniil Gentili) + . Added #[\DelayedTargetValidation] attribute. (DanielEScherzer) + . Support #[\Deprecated] on traits. (DanielEScherzer) - BCMath: . Simplify `bc_divide()` code. (SakiTakamachi) @@ -456,6 +458,7 @@ PHP NEWS (kocsismate) . Fixed bug GH-16993 (filter_var_array with FILTER_VALIDATE_INT|FILTER_NULL_ON_FAILURE should emit warning for invalid filter usage). (alexandre-daubois) + . Added FILTER_THROW_ON_FAILURE flag. (DanielEScherzer) - FPM: . Fixed bug GH-19817 (Decode SCRIPT_FILENAME issue in php 8.5).