Commit d11cbf88561 for php

commit d11cbf885617a739b8c3a8c330d1bb0a504ee5b0
Author: Claudio Dekker <1752195+claudiodekker@users.noreply.github.com>
Date:   Wed Oct 14 18:31:31 2026 +0200

    Fix array_map() optimization not resolving the class name of a static FCC (#24244)

diff --git a/NEWS b/NEWS
index e8826f49a1f..3e42a4c1d09 100644
--- a/NEWS
+++ b/NEWS
@@ -17,6 +17,8 @@ PHP                                                                        NEWS
   . Fixed skipped catch block when switching fibers during exception unwinding
     (Nicolas Grekas)
   . Fix escape analysis for objects implementing ArrayAccess. (ndossche, Arnaud)
+  . Fixed array_map() optimization not resolving the class name of a static
+    first-class callable. (Claudio Dekker)

 - Fileinfo:
   . Fixed bug GH-24289 (finfo::file() reports the wrong argument number for
diff --git a/Zend/tests/array_map_foreach_optimization_class_name.phpt b/Zend/tests/array_map_foreach_optimization_class_name.phpt
new file mode 100644
index 00000000000..fab9c51ee55
--- /dev/null
+++ b/Zend/tests/array_map_foreach_optimization_class_name.phpt
@@ -0,0 +1,74 @@
+--TEST--
+array_map(): foreach optimization resolves the class name of a static FCC
+--FILE--
+<?php
+
+namespace Foo {
+    class Bar {
+        public static function double(int $x): int {
+            return $x * 2;
+        }
+    }
+
+    class Parent_ {
+        public static function triple(int $x): int {
+            return $x * 3;
+        }
+    }
+
+    class Child extends Parent_ {
+        public static function scoped(): void {
+            var_dump(\array_map(self::triple(...), [1]));
+            var_dump(\array_map(static::triple(...), [1]));
+            var_dump(\array_map(parent::triple(...), [1]));
+        }
+    }
+
+    var_dump(\array_map(Bar::double(...), [1]));
+    Child::scoped();
+}
+
+namespace {
+    use Foo\Bar;
+    use Foo\Bar as Baz;
+
+    var_dump(array_map(Bar::double(...), [1]));
+    var_dump(array_map(Baz::double(...), [1]));
+    var_dump(array_map(\Foo\Bar::double(...), [1]));
+    var_dump(array_map(namespace\Foo\Bar::double(...), [1]));
+}
+
+?>
+--EXPECT--
+array(1) {
+  [0]=>
+  int(2)
+}
+array(1) {
+  [0]=>
+  int(3)
+}
+array(1) {
+  [0]=>
+  int(3)
+}
+array(1) {
+  [0]=>
+  int(3)
+}
+array(1) {
+  [0]=>
+  int(2)
+}
+array(1) {
+  [0]=>
+  int(2)
+}
+array(1) {
+  [0]=>
+  int(2)
+}
+array(1) {
+  [0]=>
+  int(2)
+}
diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c
index fe8f978ff59..40f90e38475 100644
--- a/Zend/zend_compile.c
+++ b/Zend/zend_compile.c
@@ -5375,7 +5375,15 @@ static zend_result zend_compile_func_array_map(znode *result, zend_ast_list *arg

 	/* Evaluate class name */
 	znode class_node;
-	if (callback->kind == ZEND_AST_STATIC_CALL) {
+	if (callback->kind == ZEND_AST_STATIC_CALL
+	 && callback->child[0]->kind == ZEND_AST_ZVAL
+	 && Z_TYPE_P(zend_ast_get_zval(callback->child[0])) == IS_STRING
+	 && zend_get_class_fetch_type(zend_ast_get_str(callback->child[0])) == ZEND_FETCH_CLASS_DEFAULT) {
+		/* Resolve the name against the namespace and imports, as compiling it
+		 * as an expression would yield the unresolved string. */
+		class_node.op_type = IS_CONST;
+		ZVAL_STR(&class_node.u.constant, zend_resolve_class_name_ast(callback->child[0]));
+	} else if (callback->kind == ZEND_AST_STATIC_CALL) {
 		znode result;
 		zend_compile_expr(&result, callback->child[0]);
 		if (result.op_type == IS_CONST || result.op_type == IS_TMP_VAR) {