diff options
author | Nikita Popov <nikita.ppv@gmail.com> | 2021-02-17 14:45:35 +0100 |
---|---|---|
committer | Nikita Popov <nikita.ppv@gmail.com> | 2021-02-18 11:39:28 +0100 |
commit | 6b0f14fe3b08be05340c6a4ae03f4131f04d882a (patch) | |
tree | 826c6b8ae8def0f858e404d104d22e573a25a5b0 /Zend/zend_closures.c | |
parent | 5d160e309ed207e618d49029e51c9c2dc2c5e61c (diff) | |
download | php-git-6b0f14fe3b08be05340c6a4ae03f4131f04d882a.tar.gz |
Fixed bug #75474
For fake closures, we need to share static variables with the
original function, not work on a separate copy. Calling a function
through Closure::fromCallable() should have the same behavior as
calling it directly.
Diffstat (limited to 'Zend/zend_closures.c')
-rw-r--r-- | Zend/zend_closures.c | 27 |
1 files changed, 20 insertions, 7 deletions
diff --git a/Zend/zend_closures.c b/Zend/zend_closures.c index c06f90fabc..38105a67f6 100644 --- a/Zend/zend_closures.c +++ b/Zend/zend_closures.c @@ -486,6 +486,11 @@ static void zend_closure_free_storage(zend_object *object) /* {{{ */ zend_object_std_dtor(&closure->std); if (closure->func.type == ZEND_USER_FUNCTION) { + /* We shared static_variables with the original function. + * Unshare now so we don't try to destroy them. */ + if (closure->func.op_array.fn_flags & ZEND_ACC_FAKE_CLOSURE) { + ZEND_MAP_PTR_INIT(closure->func.op_array.static_variables_ptr, NULL); + } destroy_op_array(&closure->func.op_array); } @@ -660,7 +665,7 @@ static ZEND_NAMED_FUNCTION(zend_closure_internal_handler) /* {{{ */ } /* }}} */ -ZEND_API void zend_create_closure(zval *res, zend_function *func, zend_class_entry *scope, zend_class_entry *called_scope, zval *this_ptr) /* {{{ */ +static void zend_create_closure_ex(zval *res, zend_function *func, zend_class_entry *scope, zend_class_entry *called_scope, zval *this_ptr, bool is_fake) /* {{{ */ { zend_closure *closure; @@ -679,12 +684,15 @@ ZEND_API void zend_create_closure(zval *res, zend_function *func, zend_class_ent closure->func.common.fn_flags |= ZEND_ACC_CLOSURE; closure->func.common.fn_flags &= ~ZEND_ACC_IMMUTABLE; - if (closure->func.op_array.static_variables) { - closure->func.op_array.static_variables = - zend_array_dup(closure->func.op_array.static_variables); + /* For fake closures, we want to reuse the static variables of the original function. */ + if (!is_fake) { + if (closure->func.op_array.static_variables) { + closure->func.op_array.static_variables = + zend_array_dup(closure->func.op_array.static_variables); + } + ZEND_MAP_PTR_INIT(closure->func.op_array.static_variables_ptr, + &closure->func.op_array.static_variables); } - ZEND_MAP_PTR_INIT(closure->func.op_array.static_variables_ptr, - &closure->func.op_array.static_variables); /* Runtime cache is scope-dependent, so we cannot reuse it if the scope changed */ if (!ZEND_MAP_PTR_GET(closure->func.op_array.run_time_cache) @@ -754,11 +762,16 @@ ZEND_API void zend_create_closure(zval *res, zend_function *func, zend_class_ent } /* }}} */ +ZEND_API void zend_create_closure(zval *res, zend_function *func, zend_class_entry *scope, zend_class_entry *called_scope, zval *this_ptr) +{ + zend_create_closure_ex(res, func, scope, called_scope, this_ptr, /* is_fake */ false); +} + ZEND_API void zend_create_fake_closure(zval *res, zend_function *func, zend_class_entry *scope, zend_class_entry *called_scope, zval *this_ptr) /* {{{ */ { zend_closure *closure; - zend_create_closure(res, func, scope, called_scope, this_ptr); + zend_create_closure_ex(res, func, scope, called_scope, this_ptr, /* is_fake */ true); closure = (zend_closure *)Z_OBJ_P(res); closure->func.common.fn_flags |= ZEND_ACC_FAKE_CLOSURE; |