Skip to content

Commit b434248

Browse files
committed
Fix GH-17126: get_class_methods omits __invoke for Closure
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
1 parent bd78496 commit b434248

2 files changed

Lines changed: 53 additions & 0 deletions

File tree

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
--TEST--
2+
GH-17126 (get_class_methods($closure) doesn't return __invoke)
3+
--FILE--
4+
<?php
5+
6+
$closure = fn (string $str) => "hello {$str}";
7+
8+
echo "from object:\n";
9+
$methods = get_class_methods($closure);
10+
sort($methods);
11+
print_r($methods);
12+
13+
echo "from class name:\n";
14+
$methods = get_class_methods('Closure');
15+
sort($methods);
16+
print_r($methods);
17+
18+
echo "unrelated class unaffected:\n";
19+
class NoInvoke { public function foo() {} }
20+
print_r(get_class_methods('NoInvoke'));
21+
22+
?>
23+
--EXPECT--
24+
from object:
25+
Array
26+
(
27+
[0] => __invoke
28+
[1] => bind
29+
[2] => bindTo
30+
[3] => call
31+
[4] => fromCallable
32+
[5] => getCurrent
33+
)
34+
from class name:
35+
Array
36+
(
37+
[0] => __invoke
38+
[1] => bind
39+
[2] => bindTo
40+
[3] => call
41+
[4] => fromCallable
42+
[5] => getCurrent
43+
)
44+
unrelated class unaffected:
45+
Array
46+
(
47+
[0] => foo
48+
)

Zend/zend_builtin_functions.c

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -945,6 +945,11 @@ ZEND_FUNCTION(get_class_methods)
945945
zend_hash_next_index_insert_new(Z_ARRVAL_P(return_value), &method_name);
946946
}
947947
} ZEND_HASH_FOREACH_END();
948+
949+
if (ce == zend_ce_closure) {
950+
ZVAL_STR_COPY(&method_name, ZSTR_KNOWN(ZEND_STR_MAGIC_INVOKE));
951+
zend_hash_next_index_insert_new(Z_ARRVAL_P(return_value), &method_name);
952+
}
948953
}
949954
/* }}} */
950955

0 commit comments

Comments
 (0)