mirror of
https://github.com/php/php-src.git
synced 2024-09-21 09:57:23 +00:00
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.
This commit is contained in:
parent
5d160e309e
commit
6b0f14fe3b
2
NEWS
2
NEWS
@ -5,6 +5,8 @@ PHP NEWS
|
||||
- Core:
|
||||
. Fixed inclusion order for phpize builds on Windows. (cmb)
|
||||
. Added missing hashtable insertion APIs for arr/obj/ref. (Sara)
|
||||
. Fixed bug #75474 (function scope static variables are not bound to a unique
|
||||
function). (Nikita)
|
||||
|
||||
- FTP:
|
||||
. Convert resource<ftp> to object \FTPConnection. (Sara)
|
||||
|
75
Zend/tests/bug75474.phpt
Normal file
75
Zend/tests/bug75474.phpt
Normal file
@ -0,0 +1,75 @@
|
||||
--TEST--
|
||||
Bug #75474: function scope static variables are not bound to a unique function
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
function bar($k, $v) {
|
||||
static $foo = [];
|
||||
$foo[$k] = $v;
|
||||
return $foo;
|
||||
}
|
||||
|
||||
var_dump(bar(0, 0));
|
||||
var_dump(Closure::fromCallable("bar")(1, 1));
|
||||
var_dump(bar(2, 2));
|
||||
var_dump(Closure::fromCallable("bar")(3, 3));
|
||||
$RF = new ReflectionFunction("bar");
|
||||
var_dump($RF->getClosure()(4, 4));
|
||||
var_dump(bar(5, 5));
|
||||
|
||||
?>
|
||||
--EXPECT--
|
||||
array(1) {
|
||||
[0]=>
|
||||
int(0)
|
||||
}
|
||||
array(2) {
|
||||
[0]=>
|
||||
int(0)
|
||||
[1]=>
|
||||
int(1)
|
||||
}
|
||||
array(3) {
|
||||
[0]=>
|
||||
int(0)
|
||||
[1]=>
|
||||
int(1)
|
||||
[2]=>
|
||||
int(2)
|
||||
}
|
||||
array(4) {
|
||||
[0]=>
|
||||
int(0)
|
||||
[1]=>
|
||||
int(1)
|
||||
[2]=>
|
||||
int(2)
|
||||
[3]=>
|
||||
int(3)
|
||||
}
|
||||
array(5) {
|
||||
[0]=>
|
||||
int(0)
|
||||
[1]=>
|
||||
int(1)
|
||||
[2]=>
|
||||
int(2)
|
||||
[3]=>
|
||||
int(3)
|
||||
[4]=>
|
||||
int(4)
|
||||
}
|
||||
array(6) {
|
||||
[0]=>
|
||||
int(0)
|
||||
[1]=>
|
||||
int(1)
|
||||
[2]=>
|
||||
int(2)
|
||||
[3]=>
|
||||
int(3)
|
||||
[4]=>
|
||||
int(4)
|
||||
[5]=>
|
||||
int(5)
|
||||
}
|
@ -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;
|
||||
|
Loading…
Reference in New Issue
Block a user