Commit 8c90743609a for php.net

commit 8c90743609a0701bd0d4ed931f6cf6a073a69cf9
Author: Ilia Alshanetsky <ilia@ilia.ws>
Date:   Wed Aug 12 06:39:54 2026 -0400

    Fix GH-17126: get_class_methods omits __invoke for Closure (#21879)

    Closure::__invoke is documented and reachable via method_exists() and
    ReflectionClass (getMethod, hasMethod, getMethods), but
    get_class_methods() walks ce->function_table directly and Closure does
    not register __invoke there: the parameter list is materialized
    per-instance by zend_get_closure_invoke_method().

    Append __invoke to the result when ce == zend_ce_closure, the same
    predicate is_closure_invoke() already uses in ext/reflection.

    Closes GH-17126

diff --git a/Zend/tests/get_class_methods/gh17126.phpt b/Zend/tests/get_class_methods/gh17126.phpt
new file mode 100644
index 00000000000..94cc28a8a9b
--- /dev/null
+++ b/Zend/tests/get_class_methods/gh17126.phpt
@@ -0,0 +1,48 @@
+--TEST--
+GH-17126 (get_class_methods($closure) doesn't return __invoke)
+--FILE--
+<?php
+
+$closure = fn (string $str) => "hello {$str}";
+
+echo "from object:\n";
+$methods = get_class_methods($closure);
+sort($methods);
+print_r($methods);
+
+echo "from class name:\n";
+$methods = get_class_methods('Closure');
+sort($methods);
+print_r($methods);
+
+echo "unrelated class unaffected:\n";
+class NoInvoke { public function foo() {} }
+print_r(get_class_methods('NoInvoke'));
+
+?>
+--EXPECT--
+from object:
+Array
+(
+    [0] => __invoke
+    [1] => bind
+    [2] => bindTo
+    [3] => call
+    [4] => fromCallable
+    [5] => getCurrent
+)
+from class name:
+Array
+(
+    [0] => __invoke
+    [1] => bind
+    [2] => bindTo
+    [3] => call
+    [4] => fromCallable
+    [5] => getCurrent
+)
+unrelated class unaffected:
+Array
+(
+    [0] => foo
+)
diff --git a/Zend/zend_builtin_functions.c b/Zend/zend_builtin_functions.c
index 860413f8411..6aa47abd2c6 100644
--- a/Zend/zend_builtin_functions.c
+++ b/Zend/zend_builtin_functions.c
@@ -957,6 +957,11 @@ ZEND_FUNCTION(get_class_methods)
 			zend_hash_next_index_insert_new(Z_ARRVAL_P(return_value), &method_name);
 		}
 	} ZEND_HASH_FOREACH_END();
+
+	if (ce == zend_ce_closure) {
+		ZVAL_STR_COPY(&method_name, ZSTR_KNOWN(ZEND_STR_MAGIC_INVOKE));
+		zend_hash_next_index_insert_new(Z_ARRVAL_P(return_value), &method_name);
+	}
 }
 /* }}} */