Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 9 additions & 2 deletions ext/reflection/php_reflection.c
Original file line number Diff line number Diff line change
Expand Up @@ -3447,12 +3447,19 @@ static void reflection_method_invoke(INTERNAL_FUNCTION_PARAMETERS, int variadic)
/* For Closure::__invoke(), closures from different source locations have
* different signatures, so we must reject those. However, closures created
* from the same source (e.g. in a loop) share the same op_array and should
* be allowed. Compare the underlying function pointer via op_array. */
* be allowed. For user closures compare op_array.opcodes, for internal
* closures (e.g. var_dump(...)) compare the handler pointer. */
if (obj_ce == zend_ce_closure && !Z_ISUNDEF(intern->obj)
&& Z_OBJ_P(object) != Z_OBJ(intern->obj)) {
const zend_function *orig_func = zend_get_closure_method_def(Z_OBJ(intern->obj));
const zend_function *given_func = zend_get_closure_method_def(Z_OBJ_P(object));
if (orig_func->op_array.opcodes != given_func->op_array.opcodes) {
bool same_closure;
if (orig_func->type == ZEND_USER_FUNCTION && given_func->type == ZEND_USER_FUNCTION) {
same_closure = orig_func->op_array.opcodes == given_func->op_array.opcodes;
} else {
same_closure = orig_func == given_func;
}
if (!same_closure) {
if (!variadic) {
efree(params);
}
Expand Down
22 changes: 22 additions & 0 deletions ext/reflection/tests/gh21362.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,25 @@ $m2 = new ReflectionMethod($closures[0], '__invoke');
foreach ($closures as $closure) {
var_dump($m2->invoke($closure));
}

// Internal closures (first-class callable syntax) should also be validated
$vd = var_dump(...);
$pr = print_r(...);
Comment thread
TimWolla marked this conversation as resolved.

$m3 = new ReflectionMethod($vd, '__invoke');
$m3->invoke($vd, 'internal closure OK');

// Same internal closure, different instance - should work
$vd2 = var_dump(...);
$m3->invoke($vd2, 'same internal closure OK');

// Different internal closure should throw
try {
$m3->invoke($pr, 'should not print');
echo "No exception thrown\n";
} catch (ReflectionException $e) {
echo $e->getMessage() . "\n";
}
?>
--EXPECT--
c1: foo=FOO, bar=BAR
Expand All @@ -51,3 +70,6 @@ Given Closure is not the same as the reflected Closure
int(0)
int(1)
int(2)
string(19) "internal closure OK"
string(24) "same internal closure OK"
Given Closure is not the same as the reflected Closure
Loading