Skip to content
Draft
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
4 changes: 2 additions & 2 deletions Zend/zend_API.c
Original file line number Diff line number Diff line change
Expand Up @@ -4236,9 +4236,9 @@ ZEND_API bool zend_is_callable_at_frame(
}

case IS_OBJECT:
if (!Z_OBJ_HANDLER_P(callable, get_closure) || Z_OBJ_HANDLER_P(callable, get_closure)(Z_OBJ_P(callable), &fcc->calling_scope, &fcc->function_handler, &fcc->object, 1) == FAILURE) {
if (UNEXPECTED(!zend_get_closure(Z_OBJ_P(callable), &fcc->calling_scope, &fcc->function_handler, &fcc->object, true))) {
if (error) *error = estrdup("no array or string given");
return 0;
return false;
}
fcc->called_scope = fcc->calling_scope;
fcc->closure = Z_OBJ_P(callable);
Expand Down
43 changes: 39 additions & 4 deletions Zend/zend_closures.c
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,6 @@ typedef struct _zend_closure {
ZEND_API zend_class_entry *zend_ce_closure;
static zend_object_handlers closure_handlers;

static zend_result zend_closure_get_closure(zend_object *obj, zend_class_entry **ce_ptr, zend_function **fptr_ptr, zend_object **obj_ptr, bool check_only);
static void zend_create_closure_ex(zval *res, zend_function *func, zend_class_entry *scope, zend_class_entry *called_scope, zend_object *this_ptr, bool is_fake, uint32_t flags);

static inline uint32_t zend_closure_flags(const zend_closure *closure)
Expand All @@ -68,11 +67,14 @@ ZEND_METHOD(Closure, __invoke) /* {{{ */
Z_PARAM_VARIADIC_WITH_NAMED(args, num_args, named_args)
ZEND_PARSE_PARAMETERS_END();

zend_fcall_info_cache fcc = {
zend_closure *closure = (zend_closure*)Z_OBJ_P(ZEND_THIS);
const zend_fcall_info_cache fcc = {
.function_handler = &closure->func,
.calling_scope = closure->called_scope,
.called_scope = closure->called_scope,
.object = closure->this_ptr,
.closure = Z_OBJ_P(ZEND_THIS),
};
zend_closure_get_closure(Z_OBJ_P(ZEND_THIS), &fcc.calling_scope, &fcc.function_handler, &fcc.object, false);
fcc.called_scope = fcc.calling_scope;
zend_call_known_fcc(&fcc, return_value, num_args, args, named_args);

/* destruct the function also, then - we have allocated it in get_method */
Expand Down Expand Up @@ -647,6 +649,39 @@ static zend_result zend_closure_get_closure(zend_object *obj, zend_class_entry *
}
/* }}} */

ZEND_API bool zend_get_closure(zend_object *obj, zend_class_entry **ce_ptr, zend_function **fptr_ptr, zend_object **this_ptr, bool check_only) {
/* Fast path for native closures */
if (EXPECTED(obj->ce == zend_ce_closure)) {
zend_closure *closure = (zend_closure*)obj;

*fptr_ptr = &closure->func;
*ce_ptr = closure->called_scope;
*this_ptr = closure->this_ptr;
return true;
}

if (EXPECTED(obj->handlers->get_closure == zend_std_get_closure)) {
/* Inline zend_std_get_closure behaviour for fast path of __invoke() methods */
zend_class_entry *ce = obj->ce;
zend_function *func = zend_hash_find_ex_ptr(&ce->function_table, ZSTR_KNOWN(ZEND_STR_MAGIC_INVOKE), /* known_hash */ true);

if (UNEXPECTED(func == NULL)) {
return false;
}
*fptr_ptr = func;
*ce_ptr = ce;
*this_ptr = obj;
return true;
}

if (UNEXPECTED(obj->handlers->get_closure == NULL)) {
return false;
}

/* Currently only FFI implements a custom get_closure */
return obj->handlers->get_closure(obj, ce_ptr, fptr_ptr, this_ptr, check_only) == SUCCESS;
}

/* *is_temp is int due to Object Handler API */
static HashTable *zend_closure_get_debug_info(zend_object *object, int *is_temp) /* {{{ */
{
Expand Down
1 change: 1 addition & 0 deletions Zend/zend_closures.h
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ ZEND_API void zend_create_partial_closure(zval *res, zend_function *func, zend_c
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_get_closure(zend_object *obj, zend_class_entry **ce_ptr, zend_function **fptr_ptr, zend_object **this_ptr, bool check_only);

END_EXTERN_C()

Expand Down
44 changes: 21 additions & 23 deletions Zend/zend_execute.c
Original file line number Diff line number Diff line change
Expand Up @@ -5175,33 +5175,31 @@ static zend_never_inline zend_execute_data *zend_init_dynamic_call_object(zend_o
zend_object *object;
uint32_t call_info;

if (EXPECTED(function->handlers->get_closure) &&
EXPECTED(function->handlers->get_closure(function, &called_scope, &fbc, &object, 0) == SUCCESS)) {

object_or_called_scope = called_scope;
if (EXPECTED(fbc->common.fn_flags & ZEND_ACC_CLOSURE)) {
/* Delay closure destruction until its invocation */
GC_ADDREF(ZEND_CLOSURE_OBJECT(fbc));
ZEND_ASSERT(ZEND_ACC_FAKE_CLOSURE == ZEND_CALL_FAKE_CLOSURE);
call_info = ZEND_CALL_NESTED_FUNCTION | ZEND_CALL_DYNAMIC | ZEND_CALL_CLOSURE |
(fbc->common.fn_flags & ZEND_ACC_FAKE_CLOSURE);
if (object) {
call_info |= ZEND_CALL_HAS_THIS;
object_or_called_scope = object;
}
} else {
call_info = ZEND_CALL_NESTED_FUNCTION | ZEND_CALL_DYNAMIC;
if (object) {
call_info |= ZEND_CALL_RELEASE_THIS | ZEND_CALL_HAS_THIS;
GC_ADDREF(object); /* For $this pointer */
object_or_called_scope = object;
}
}
} else {
if (UNEXPECTED(!zend_get_closure(function, &called_scope, &fbc, &object, false))) {
zend_throw_error(NULL, "Object of type %s is not callable", ZSTR_VAL(function->ce->name));
return NULL;
}

object_or_called_scope = called_scope;
if (EXPECTED(fbc->common.fn_flags & ZEND_ACC_CLOSURE)) {
/* Delay closure destruction until its invocation */
GC_ADDREF(ZEND_CLOSURE_OBJECT(fbc));
ZEND_ASSERT(ZEND_ACC_FAKE_CLOSURE == ZEND_CALL_FAKE_CLOSURE);
call_info = ZEND_CALL_NESTED_FUNCTION | ZEND_CALL_DYNAMIC | ZEND_CALL_CLOSURE |
(fbc->common.fn_flags & ZEND_ACC_FAKE_CLOSURE);
if (object) {
call_info |= ZEND_CALL_HAS_THIS;
object_or_called_scope = object;
}
} else {
call_info = ZEND_CALL_NESTED_FUNCTION | ZEND_CALL_DYNAMIC;
if (object) {
call_info |= ZEND_CALL_RELEASE_THIS | ZEND_CALL_HAS_THIS;
GC_ADDREF(object); /* For $this pointer */
object_or_called_scope = object;
}
}

if (EXPECTED(fbc->type == ZEND_USER_FUNCTION) && UNEXPECTED(!RUN_TIME_CACHE(&fbc->op_array))) {
init_func_run_time_cache(&fbc->op_array);
}
Expand Down
1 change: 1 addition & 0 deletions Zend/zend_object_handlers.c
Original file line number Diff line number Diff line change
Expand Up @@ -2612,6 +2612,7 @@ ZEND_API zend_result zend_std_cast_object_tostring(zend_object *readobj, zval *w
}
/* }}} */

/* Note: changes to zend_std_get_closure should be applied to zend_get_closure() in zend_closure.c */
ZEND_API zend_result zend_std_get_closure(zend_object *obj, zend_class_entry **ce_ptr, zend_function **fptr_ptr, zend_object **obj_ptr, bool check_only) /* {{{ */
{
zend_class_entry *ce = obj->ce;
Expand Down
17 changes: 8 additions & 9 deletions ext/reflection/php_reflection.c
Original file line number Diff line number Diff line change
Expand Up @@ -1805,11 +1805,12 @@ ZEND_METHOD(ReflectionFunctionAbstract, getClosureCalledClass)
if (!Z_ISUNDEF(intern->obj)) {
zend_class_entry *called_scope;
zend_function *closure_func;
zend_object *object;
if (Z_OBJ_HANDLER(intern->obj, get_closure)
&& Z_OBJ_HANDLER(intern->obj, get_closure)(Z_OBJ(intern->obj), &called_scope, &closure_func, &object, true) == SUCCESS
&& closure_func && (called_scope || closure_func->common.scope)
) {
zend_object *this_ptr;

if (zend_get_closure(Z_OBJ(intern->obj), &called_scope, &closure_func, &this_ptr, true)
/* if zend_get_closure succeeds we have a non-NULL closure_func */
&& (called_scope || closure_func->common.scope)) {
ZEND_ASSERT(closure_func);
zend_reflection_class_factory(called_scope ? called_scope : closure_func->common.scope, return_value);
}
}
Expand Down Expand Up @@ -2062,8 +2063,7 @@ ZEND_METHOD(ReflectionFunction, invoke)
fcc.object = NULL;

if (!Z_ISUNDEF(intern->obj)) {
Z_OBJ_HT(intern->obj)->get_closure(
Z_OBJ(intern->obj), &fcc.called_scope, &fcc.function_handler, &fcc.object, false);
zend_get_closure(Z_OBJ(intern->obj), &fcc.calling_scope, &fcc.function_handler, &fcc.object, false);
}

zval retval;
Expand Down Expand Up @@ -2095,8 +2095,7 @@ ZEND_METHOD(ReflectionFunction, invokeArgs)
fcc.object = NULL;

if (!Z_ISUNDEF(intern->obj)) {
Z_OBJ_HT(intern->obj)->get_closure(
Z_OBJ(intern->obj), &fcc.called_scope, &fcc.function_handler, &fcc.object, false);
zend_get_closure(Z_OBJ(intern->obj), &fcc.called_scope, &fcc.function_handler, &fcc.object, false);
}

zval retval;
Expand Down
Loading