Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
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
48 changes: 6 additions & 42 deletions Zend/zend_API.c
Original file line number Diff line number Diff line change
Expand Up @@ -4100,51 +4100,15 @@ ZEND_API zend_string *zend_get_callable_name_ex(const zval *callable, const zend
}
/* }}} */

static bool zend_fcc_function_handler_equals(const zend_function *func1, const zend_function *func2) /* {{{ */
ZEND_API bool zend_fcc_closure_equals_ex(const zend_fcall_info_cache* a, const zend_fcall_info_cache* b)
{
if (func1 == func2) {
return true;
}

const bool fake_closure1 = (func1->common.fn_flags & ZEND_ACC_FAKE_CLOSURE) != 0;
const bool fake_closure2 = (func2->common.fn_flags & ZEND_ACC_FAKE_CLOSURE) != 0;

if (!fake_closure1 && !fake_closure2) {
return false;
}
if (((func1->common.fn_flags & ZEND_ACC_CLOSURE) && !fake_closure1) ||
((func2->common.fn_flags & ZEND_ACC_CLOSURE) && !fake_closure2)) {
return false;
}
if (func1->type != func2->type ||
func1->common.scope != func2->common.scope ||
!zend_string_equals(func1->common.function_name, func2->common.function_name)) {
return false;
}

if (func1->type == ZEND_USER_FUNCTION) {
return func1->op_array.opcodes == func2->op_array.opcodes;
}

return func1->internal_function.handler == func2->internal_function.handler;
}
/* }}} */

ZEND_API bool zend_fcc_closure_equals_ex(const zend_fcall_info_cache* a, const zend_fcall_info_cache* b) /* {{{ */
{
const zend_function *func1 = a->function_handler;
const zend_function *func2 = b->function_handler;
const zend_function *fn_a = a->function_handler;
const zend_object *closure_zobj_a = a->closure && a->closure->ce == zend_ce_closure ? a->closure : NULL;
const zend_function *fn_b = b->function_handler;
const zend_object *closure_zobj_b = b->closure && b->closure->ce == zend_ce_closure ? b->closure : NULL;

if (a->closure && a->closure->ce == zend_ce_closure) {
func1 = zend_get_closure_method_def(a->closure);
}
if (b->closure && b->closure->ce == zend_ce_closure) {
func2 = zend_get_closure_method_def(b->closure);
}

return zend_fcc_function_handler_equals(func1, func2);
return zend_fn_closure_equals_ex(fn_a, closure_zobj_a, fn_b, closure_zobj_b);
}
/* }}} */

ZEND_API bool zend_is_callable_at_frame(
const zval *callable, zend_object *object, const zend_execute_data *frame,
Expand Down
55 changes: 55 additions & 0 deletions Zend/zend_closures.c
Original file line number Diff line number Diff line change
Expand Up @@ -535,6 +535,61 @@ static int zend_closure_compare(zval *o1, zval *o2) /* {{{ */
}
/* }}} */

ZEND_API bool zend_fn_closure_equals_ex(
const zend_function *fn_a, const zend_object *closure_zobj_a,
const zend_function *fn_b, const zend_object *closure_zobj_b
) {
const zend_closure *closure_a = NULL;
bool is_closure_a_fake = false;

const zend_closure *closure_b = NULL;
bool is_closure_b_fake = false;

if (closure_zobj_a) {
closure_a = (const zend_closure *) closure_zobj_a;
is_closure_a_fake = zend_closure_is_fake(closure_a);
fn_a = &closure_a->func;
}
if (closure_zobj_b) {
closure_b = (const zend_closure *) closure_zobj_b;
is_closure_b_fake = zend_closure_is_fake(closure_b);
fn_b = &closure_b->func;
}

// TODO is this even possible as closure allocates a new zend_function pointer on creation?
if (fn_a == fn_b) {
return true;
}

if (!is_closure_a_fake && !is_closure_b_fake) {
return false;
}
if ((closure_a && !is_closure_a_fake) || (closure_b && !is_closure_b_fake)) {
return false;
}
if (fn_a->type != fn_b->type ||
fn_a->common.scope != fn_b->common.scope ||
!zend_string_equals(fn_a->common.function_name, fn_b->common.function_name)) {
return false;
}

if (fn_a->type == ZEND_USER_FUNCTION) {
return fn_a->op_array.opcodes == fn_b->op_array.opcodes;
} else {
zif_handler zif_a = fn_a->internal_function.handler;
zif_handler zif_b = fn_b->internal_function.handler;

if (closure_a) {
zif_a = closure_a->orig_internal_handler;
}
if (closure_b) {
zif_b = closure_b->orig_internal_handler;
}

return zif_a == zif_b;
}
}

ZEND_API zend_function *zend_get_closure_invoke_method(zend_object *object) /* {{{ */
{
zend_closure *closure = (zend_closure *)object;
Expand Down
5 changes: 5 additions & 0 deletions Zend/zend_closures.h
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,11 @@ ZEND_API zend_function *zend_get_closure_invoke_method(zend_object *obj);
ZEND_API const zend_function *zend_get_closure_method_def(zend_object *obj);
ZEND_API zend_object* zend_get_closure_this_ptr(zval *obj);

ZEND_API bool zend_fn_closure_equals_ex(
const zend_function *fn_a, const zend_object *closure_zobj_a,
const zend_function *fn_b, const zend_object *closure_zobj_b
);

END_EXTERN_C()

#endif
27 changes: 27 additions & 0 deletions ext/spl/tests/autoloading/gh22118_internal_fake_closures.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
--TEST--
GH-22118: spl_autoload_unregister() should also unregister fake closure representing internal function
--FILE--
<?php

spl_autoload_register(strlen(...));
var_dump(spl_autoload_functions());
var_dump(spl_autoload_unregister('strlen'));
var_dump(spl_autoload_functions());

?>
--EXPECT--
array(1) {
[0]=>
object(Closure)#1 (2) {
["function"]=>
string(6) "strlen"
["parameter"]=>
array(1) {
["$string"]=>
string(10) "<required>"
}
}
}
bool(true)
array(0) {
}
Loading