Commit e3ff9f10a24 for php

commit e3ff9f10a246cc8ed12964991ce3fc89a3cb2df0
Author: Alexander Danilov <10532067+adapik@users.noreply.github.com>
Date:   Thu Oct 15 16:20:00 2026 +0300

    Fix memory leak in array_map optimization (#24138)

diff --git a/NEWS b/NEWS
index 3e42a4c1d09..cd3a34557f9 100644
--- a/NEWS
+++ b/NEWS
@@ -134,6 +134,8 @@ PHP                                                                        NEWS
     CLOCK_MONOTONIC_RAW. (nicolas-grekas)
   . Fixed array_map optimization with non-literal function or non-literal args.
     (Arnaud)
+  . Fixed memory leak in array_map() foreach optimization when a constant
+    non-array string argument is passed. (Alexander Danilov)
   . Fixed use-after-free when a dl()-loaded extension declares a frameless
     function and a later request calls it. (Ilia Alshanetsky)
   . Fixed System V shared memory emulation for Windows attaching segments past
diff --git a/Zend/tests/array_map_foreach_optimization_const_non_array.phpt b/Zend/tests/array_map_foreach_optimization_const_non_array.phpt
new file mode 100644
index 00000000000..5ccfbdd7b64
--- /dev/null
+++ b/Zend/tests/array_map_foreach_optimization_const_non_array.phpt
@@ -0,0 +1,32 @@
+--TEST--
+array_map(): foreach optimization - constant non-array argument does not leak
+--FILE--
+<?php
+
+$tests = [
+    'string' => fn() => array_map(strtoupper(...), "str"),
+    'int' => fn() => array_map(strtoupper(...), 1),
+    'float' => fn() => array_map(strtoupper(...), 1.5),
+    'true' => fn() => array_map(strtoupper(...), true),
+    'null' => fn() => array_map(strtoupper(...), null),
+    'folded string' => fn() => array_map(strtoupper(...), "a" . "b"),
+    'static call' => fn() => array_map(Foo::bar(...), "str"),
+];
+
+foreach ($tests as $name => $test) {
+    try {
+        $test();
+    } catch (Throwable $e) {
+        echo $name, ': ', get_class($e), ': ', $e->getMessage(), "\n";
+    }
+}
+
+?>
+--EXPECT--
+string: TypeError: array_map(): Argument #2 ($array) must be of type array, string given
+int: TypeError: array_map(): Argument #2 ($array) must be of type array, int given
+float: TypeError: array_map(): Argument #2 ($array) must be of type array, float given
+true: TypeError: array_map(): Argument #2 ($array) must be of type array, true given
+null: TypeError: array_map(): Argument #2 ($array) must be of type array, null given
+folded string: TypeError: array_map(): Argument #2 ($array) must be of type array, string given
+static call: Error: Class "Foo" not found
diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c
index 40f90e38475..2fbcf1ad2c0 100644
--- a/Zend/zend_compile.c
+++ b/Zend/zend_compile.c
@@ -5415,15 +5415,7 @@ static zend_result zend_compile_func_array_map(znode *result, zend_ast_list *arg
 	zend_ast *call_args = zend_partial_apply(callback,
 			zend_ast_create_znode(&value));
 	if (!call_args) {
-		CG(active_op_array)->T--;
-		if (func_node.op_type == IS_CONST) {
-			zval_ptr_dtor_nogc(&func_node.u.constant);
-		}
-		if (class_node.op_type == IS_CONST) {
-			zval_ptr_dtor_nogc(&class_node.u.constant);
-		}
-		/* The callback is not a FCC/PFA, or is not optimizable */
-		return FAILURE;
+		goto fail;
 	}

 	zend_op *opline;
@@ -5432,6 +5424,10 @@ static zend_result zend_compile_func_array_map(znode *result, zend_ast_list *arg
 	zend_compile_expr(&array, args->child[1]);
 	/* array is an argument to both ZEND_TYPE_ASSERT and to ZEND_FE_RESET_R. */
 	if (array.op_type == IS_CONST) {
+		if (Z_TYPE(array.u.constant) != IS_ARRAY) {
+			zval_ptr_dtor_nogc(&array.u.constant);
+			goto fail;
+		}
 		Z_TRY_ADDREF(array.u.constant);
 	}

@@ -5512,6 +5508,17 @@ static zend_result zend_compile_func_array_map(znode *result, zend_ast_list *arg
 	}

 	return SUCCESS;
+
+fail:
+	CG(active_op_array)->T--;
+	if (func_node.op_type == IS_CONST) {
+		zval_ptr_dtor_nogc(&func_node.u.constant);
+	}
+	if (class_node.op_type == IS_CONST) {
+		zval_ptr_dtor_nogc(&class_node.u.constant);
+	}
+
+	return FAILURE;
 }

 static zend_result zend_try_compile_special_func_ex(znode *result, zend_string *lcname, zend_ast_list *args, uint32_t type, uint32_t lineno) /* {{{ */