diff --git a/Zend/zend_API.c b/Zend/zend_API.c index 5ef6bbddc3bf..2007796e494f 100644 --- a/Zend/zend_API.c +++ b/Zend/zend_API.c @@ -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, diff --git a/Zend/zend_closures.c b/Zend/zend_closures.c index d4c0b5881369..4862e42017e7 100644 --- a/Zend/zend_closures.c +++ b/Zend/zend_closures.c @@ -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; diff --git a/Zend/zend_closures.h b/Zend/zend_closures.h index c421c100833a..1d3fc89c1dfd 100644 --- a/Zend/zend_closures.h +++ b/Zend/zend_closures.h @@ -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 diff --git a/ext/spl/tests/autoloading/gh22118_internal_fake_closures.phpt b/ext/spl/tests/autoloading/gh22118_internal_fake_closures.phpt new file mode 100644 index 000000000000..085e0fd6d6bb --- /dev/null +++ b/ext/spl/tests/autoloading/gh22118_internal_fake_closures.phpt @@ -0,0 +1,27 @@ +--TEST-- +GH-22118: spl_autoload_unregister() should also unregister fake closure representing internal function +--FILE-- + +--EXPECT-- +array(1) { + [0]=> + object(Closure)#1 (2) { + ["function"]=> + string(6) "strlen" + ["parameter"]=> + array(1) { + ["$string"]=> + string(10) "" + } + } +} +bool(true) +array(0) { +}