summaryrefslogtreecommitdiff
path: root/Zend/zend_closures.c
diff options
context:
space:
mode:
authorNikita Popov <nikita.ppv@gmail.com>2021-02-17 14:45:35 +0100
committerNikita Popov <nikita.ppv@gmail.com>2021-02-18 11:39:28 +0100
commit6b0f14fe3b08be05340c6a4ae03f4131f04d882a (patch)
tree826c6b8ae8def0f858e404d104d22e573a25a5b0 /Zend/zend_closures.c
parent5d160e309ed207e618d49029e51c9c2dc2c5e61c (diff)
downloadphp-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.c27
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;