Skip to content

Commit db545b3

Browse files
committed
Fix static variable behavior in methods
1 parent bbb86ba commit db545b3

18 files changed

+64
-153
lines changed

Zend/tests/anon/015.phpt

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,10 +19,7 @@ var_dump($d->foo(24));
1919
var_dump($c->foo());
2020
?>
2121
--EXPECT--
22-
array(1) {
23-
[0]=>
24-
int(42)
25-
}
22+
NULL
2623
array(1) {
2724
[0]=>
2825
int(24)

Zend/tests/anon/016.phpt

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -19,11 +19,7 @@ var_dump($d->foo(24));
1919
var_dump($c->foo());
2020
?>
2121
--EXPECT--
22-
array(1) {
23-
[0]=>
24-
object(stdClass)#2 (0) {
25-
}
26-
}
22+
NULL
2723
array(1) {
2824
[0]=>
2925
int(24)

Zend/tests/method_static_var.phpt

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,6 @@ Initial value of static var in method depends on the include time of the class d
33
--FILE--
44
<?php
55

6-
/* The current behavior is probably a bug, but we should still test how it currently works. */
7-
86
class Foo {
97
public static function test() {
108
static $i = 0;
@@ -22,5 +20,5 @@ Bar::test();
2220
--EXPECT--
2321
int(1)
2422
int(2)
23+
int(1)
2524
int(2)
26-
int(3)

Zend/zend_closures.c

Lines changed: 14 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ typedef struct _zend_closure {
3838
zend_function func;
3939
zval this_ptr;
4040
zend_class_entry *called_scope;
41+
HashTable *static_variables;
4142
zif_handler orig_internal_handler;
4243
} zend_closure;
4344

@@ -516,6 +517,11 @@ static zend_object *zend_closure_clone(zend_object *zobject) /* {{{ */
516517

517518
zend_create_closure(&result, &closure->func,
518519
closure->func.common.scope, closure->called_scope, &closure->this_ptr);
520+
if (closure->static_variables) {
521+
zend_closure *new_closure = (zend_closure *) Z_OBJ(result);
522+
zend_array_destroy(new_closure->static_variables);
523+
new_closure->static_variables = zend_array_dup(closure->static_variables);
524+
}
519525
return Z_OBJ(result);
520526
}
521527
/* }}} */
@@ -549,11 +555,9 @@ static HashTable *zend_closure_get_debug_info(zend_object *object, int *is_temp)
549555

550556
debug_info = zend_new_array(8);
551557

552-
if (closure->func.type == ZEND_USER_FUNCTION && closure->func.op_array.static_variables) {
558+
if (closure->func.type == ZEND_USER_FUNCTION && closure->static_variables) {
553559
zval *var;
554-
HashTable *static_variables =
555-
ZEND_MAP_PTR_GET(closure->func.op_array.static_variables_ptr);
556-
ZVAL_ARR(&val, zend_array_dup(static_variables));
560+
ZVAL_ARR(&val, zend_array_dup(closure->static_variables));
557561
zend_hash_update(debug_info, ZSTR_KNOWN(ZEND_STR_STATIC), &val);
558562
ZEND_HASH_FOREACH_VAL(Z_ARRVAL(val), var) {
559563
if (Z_TYPE_P(var) == IS_CONSTANT_AST) {
@@ -615,8 +619,7 @@ static HashTable *zend_closure_get_gc(zend_object *obj, zval **table, int *n) /*
615619

616620
*table = Z_TYPE(closure->this_ptr) != IS_NULL ? &closure->this_ptr : NULL;
617621
*n = Z_TYPE(closure->this_ptr) != IS_NULL ? 1 : 0;
618-
return (closure->func.type == ZEND_USER_FUNCTION) ?
619-
ZEND_MAP_PTR_GET(closure->func.op_array.static_variables_ptr) : NULL;
622+
return closure->static_variables;
620623
}
621624
/* }}} */
622625

@@ -680,11 +683,10 @@ ZEND_API void zend_create_closure(zval *res, zend_function *func, zend_class_ent
680683
closure->func.common.fn_flags &= ~ZEND_ACC_IMMUTABLE;
681684

682685
if (closure->func.op_array.static_variables) {
683-
closure->func.op_array.static_variables =
684-
zend_array_dup(closure->func.op_array.static_variables);
686+
ZEND_MAP_PTR_INIT(closure->func.op_array.static_variables_ptr,
687+
&closure->static_variables);
688+
closure->static_variables = zend_array_dup(closure->func.op_array.static_variables);
685689
}
686-
ZEND_MAP_PTR_INIT(closure->func.op_array.static_variables_ptr,
687-
&closure->func.op_array.static_variables);
688690

689691
/* Runtime cache is scope-dependent, so we cannot reuse it if the scope changed */
690692
if (!ZEND_MAP_PTR_GET(closure->func.op_array.run_time_cache)
@@ -768,15 +770,14 @@ ZEND_API void zend_create_fake_closure(zval *res, zend_function *func, zend_clas
768770
void zend_closure_bind_var(zval *closure_zv, zend_string *var_name, zval *var) /* {{{ */
769771
{
770772
zend_closure *closure = (zend_closure *) Z_OBJ_P(closure_zv);
771-
HashTable *static_variables = ZEND_MAP_PTR_GET(closure->func.op_array.static_variables_ptr);
772-
zend_hash_update(static_variables, var_name, var);
773+
zend_hash_update(closure->static_variables, var_name, var);
773774
}
774775
/* }}} */
775776

776777
void zend_closure_bind_var_ex(zval *closure_zv, uint32_t offset, zval *val) /* {{{ */
777778
{
778779
zend_closure *closure = (zend_closure *) Z_OBJ_P(closure_zv);
779-
HashTable *static_variables = ZEND_MAP_PTR_GET(closure->func.op_array.static_variables_ptr);
780+
HashTable *static_variables = closure->static_variables;
780781
zval *var = (zval*)((char*)static_variables->arData + offset);
781782
zval_ptr_dtor(var);
782783
ZVAL_COPY_VALUE(var, val);

Zend/zend_compile.c

Lines changed: 14 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1029,28 +1029,32 @@ static uint32_t zend_add_try_element(uint32_t try_op) /* {{{ */
10291029
}
10301030
/* }}} */
10311031

1032+
void zend_init_static_variables_map_ptr(zend_op_array *op_array)
1033+
{
1034+
if (op_array->static_variables) {
1035+
ZEND_MAP_PTR_INIT(op_array->static_variables_ptr,
1036+
zend_arena_alloc(&CG(arena), sizeof(HashTable *)));
1037+
ZEND_MAP_PTR_SET(op_array->static_variables_ptr, NULL);
1038+
}
1039+
}
1040+
10321041
ZEND_API void function_add_ref(zend_function *function) /* {{{ */
10331042
{
10341043
if (function->type == ZEND_USER_FUNCTION) {
10351044
zend_op_array *op_array = &function->op_array;
1036-
10371045
if (op_array->refcount) {
10381046
(*op_array->refcount)++;
10391047
}
1040-
if (op_array->static_variables
1041-
&& !(GC_FLAGS(op_array->static_variables) & IS_ARRAY_IMMUTABLE)) {
1042-
GC_ADDREF(op_array->static_variables);
1043-
}
10441048

10451049
if (CG(compiler_options) & ZEND_COMPILE_PRELOAD) {
10461050
ZEND_ASSERT(op_array->fn_flags & ZEND_ACC_PRELOADED);
10471051
ZEND_MAP_PTR_NEW(op_array->run_time_cache);
1048-
ZEND_MAP_PTR_NEW(op_array->static_variables_ptr);
10491052
} else {
1050-
ZEND_MAP_PTR_INIT(op_array->static_variables_ptr, &op_array->static_variables);
1051-
ZEND_MAP_PTR_INIT(op_array->run_time_cache, zend_arena_alloc(&CG(arena), sizeof(void*)));
1053+
ZEND_MAP_PTR_INIT(op_array->run_time_cache, zend_arena_alloc(&CG(arena), sizeof(void *)));
10521054
ZEND_MAP_PTR_SET(op_array->run_time_cache, NULL);
10531055
}
1056+
1057+
zend_init_static_variables_map_ptr(op_array);
10541058
}
10551059

10561060
if (function->common.function_name) {
@@ -7021,9 +7025,8 @@ void zend_compile_func_decl(znode *result, zend_ast *ast, bool toplevel) /* {{{
70217025
if (CG(compiler_options) & ZEND_COMPILE_PRELOAD) {
70227026
op_array->fn_flags |= ZEND_ACC_PRELOADED;
70237027
ZEND_MAP_PTR_NEW(op_array->run_time_cache);
7024-
ZEND_MAP_PTR_NEW(op_array->static_variables_ptr);
70257028
} else {
7026-
ZEND_MAP_PTR_INIT(op_array->run_time_cache, zend_arena_alloc(&CG(arena), sizeof(void*)));
7029+
ZEND_MAP_PTR_INIT(op_array->run_time_cache, zend_arena_alloc(&CG(arena), sizeof(void *)));
70277030
ZEND_MAP_PTR_SET(op_array->run_time_cache, NULL);
70287031
}
70297032

@@ -7112,6 +7115,7 @@ void zend_compile_func_decl(znode *result, zend_ast *ast, bool toplevel) /* {{{
71127115
zend_do_extended_stmt();
71137116
zend_emit_final_return(0);
71147117

7118+
zend_init_static_variables_map_ptr(op_array);
71157119
pass_two(CG(active_op_array));
71167120
zend_oparray_context_end(&orig_oparray_context);
71177121

Zend/zend_compile.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -795,6 +795,7 @@ void zend_verify_namespace(void);
795795
void zend_resolve_goto_label(zend_op_array *op_array, zend_op *opline);
796796

797797
ZEND_API void function_add_ref(zend_function *function);
798+
void zend_init_static_variables_map_ptr(zend_op_array *op_array);
798799

799800
#define INITIAL_OP_ARRAY_SIZE 64
800801

Zend/zend_execute_API.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -285,7 +285,7 @@ void shutdown_executor(void) /* {{{ */
285285
if (op_array->type == ZEND_INTERNAL_FUNCTION) {
286286
break;
287287
}
288-
if (op_array->static_variables) {
288+
if (ZEND_MAP_PTR(op_array->static_variables_ptr)) {
289289
HashTable *ht = ZEND_MAP_PTR_GET(op_array->static_variables_ptr);
290290
if (ht) {
291291
zend_array_release(ht);
@@ -308,7 +308,7 @@ void shutdown_executor(void) /* {{{ */
308308
zend_op_array *op_array;
309309
ZEND_HASH_FOREACH_PTR(&ce->function_table, op_array) {
310310
if (op_array->type == ZEND_USER_FUNCTION) {
311-
if (op_array->static_variables) {
311+
if (ZEND_MAP_PTR(op_array->static_variables_ptr)) {
312312
HashTable *ht = ZEND_MAP_PTR_GET(op_array->static_variables_ptr);
313313
if (ht) {
314314
zend_array_release(ht);

Zend/zend_inheritance.c

Lines changed: 9 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -89,28 +89,10 @@ static zend_function *zend_duplicate_internal_function(zend_function *func, zend
8989

9090
static zend_function *zend_duplicate_user_function(zend_function *func) /* {{{ */
9191
{
92-
zend_function *new_function;
93-
94-
new_function = zend_arena_alloc(&CG(arena), sizeof(zend_op_array));
95-
memcpy(new_function, func, sizeof(zend_op_array));
96-
97-
if (CG(compiler_options) & ZEND_COMPILE_PRELOAD) {
98-
ZEND_ASSERT(new_function->op_array.fn_flags & ZEND_ACC_PRELOADED);
99-
ZEND_MAP_PTR_NEW(new_function->op_array.static_variables_ptr);
100-
} else {
101-
ZEND_MAP_PTR_INIT(new_function->op_array.static_variables_ptr, &new_function->op_array.static_variables);
102-
}
103-
104-
HashTable *static_properties_ptr = ZEND_MAP_PTR_GET(func->op_array.static_variables_ptr);
105-
if (static_properties_ptr) {
106-
/* See: Zend/tests/method_static_var.phpt */
107-
ZEND_MAP_PTR_SET(new_function->op_array.static_variables_ptr, static_properties_ptr);
108-
GC_TRY_ADDREF(static_properties_ptr);
109-
} else {
110-
GC_TRY_ADDREF(new_function->op_array.static_variables);
111-
}
112-
113-
return new_function;
92+
zend_op_array *new_op_array = zend_arena_alloc(&CG(arena), sizeof(zend_op_array));
93+
memcpy(new_op_array, func, sizeof(zend_op_array));
94+
zend_init_static_variables_map_ptr(new_op_array);
95+
return (zend_function *) new_op_array;
11496
}
11597
/* }}} */
11698

@@ -2504,20 +2486,23 @@ static zend_class_entry *zend_lazy_class_load(zend_class_entry *pce)
25042486
for (; p != end; p++) {
25052487
zend_op_array *op_array, *new_op_array;
25062488
void ***run_time_cache_ptr;
2489+
HashTable **static_variables_ptr;
25072490

25082491
op_array = Z_PTR(p->val);
25092492
ZEND_ASSERT(op_array->type == ZEND_USER_FUNCTION);
25102493
ZEND_ASSERT(op_array->scope == pce);
25112494
ZEND_ASSERT(op_array->prototype == NULL);
2512-
new_op_array = zend_arena_alloc(&CG(arena), sizeof(zend_op_array) + sizeof(void*));
2495+
new_op_array = zend_arena_alloc(&CG(arena), sizeof(zend_op_array) + 2 * sizeof(void*));
25132496
Z_PTR(p->val) = new_op_array;
25142497
memcpy(new_op_array, op_array, sizeof(zend_op_array));
25152498
run_time_cache_ptr = (void***)(new_op_array + 1);
25162499
*run_time_cache_ptr = NULL;
2500+
static_variables_ptr = (HashTable **) (run_time_cache_ptr + 1);
2501+
*static_variables_ptr = NULL;
25172502
new_op_array->fn_flags &= ~ZEND_ACC_IMMUTABLE;
25182503
new_op_array->scope = ce;
25192504
ZEND_MAP_PTR_INIT(new_op_array->run_time_cache, run_time_cache_ptr);
2520-
ZEND_MAP_PTR_INIT(new_op_array->static_variables_ptr, &new_op_array->static_variables);
2505+
ZEND_MAP_PTR_INIT(new_op_array->static_variables_ptr, static_variables_ptr);
25212506

25222507
zend_update_inherited_handler(constructor);
25232508
zend_update_inherited_handler(destructor);

Zend/zend_language_scanner.l

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -636,6 +636,7 @@ static zend_op_array *zend_compile(int type)
636636
zend_emit_final_return(type == ZEND_USER_FUNCTION);
637637
op_array->line_start = 1;
638638
op_array->line_end = last_lineno;
639+
zend_init_static_variables_map_ptr(op_array);
639640
pass_two(op_array);
640641
zend_oparray_context_end(&original_oparray_context);
641642
zend_file_context_end(&original_file_context);

Zend/zend_opcode.c

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ void init_op_array(zend_op_array *op_array, zend_uchar type, int initial_ops_siz
7777
op_array->last_live_range = 0;
7878

7979
op_array->static_variables = NULL;
80-
ZEND_MAP_PTR_INIT(op_array->static_variables_ptr, &op_array->static_variables);
80+
ZEND_MAP_PTR_INIT(op_array->static_variables_ptr, NULL);
8181
op_array->last_try_catch = 0;
8282

8383
op_array->fn_flags = 0;
@@ -515,12 +515,10 @@ ZEND_API void destroy_op_array(zend_op_array *op_array)
515515
{
516516
uint32_t i;
517517

518-
if (op_array->static_variables) {
518+
if (ZEND_MAP_PTR(op_array->static_variables_ptr)) {
519519
HashTable *ht = ZEND_MAP_PTR_GET(op_array->static_variables_ptr);
520-
if (ht && !(GC_FLAGS(ht) & IS_ARRAY_IMMUTABLE)) {
521-
if (GC_DELREF(ht) == 0) {
522-
zend_array_destroy(ht);
523-
}
520+
if (ht) {
521+
zend_array_release(ht);
524522
}
525523
}
526524

@@ -599,6 +597,9 @@ ZEND_API void destroy_op_array(zend_op_array *op_array)
599597
}
600598
efree(arg_info);
601599
}
600+
if (op_array->static_variables) {
601+
zend_array_release(op_array->static_variables);
602+
}
602603
}
603604

604605
static void zend_update_extended_stmts(zend_op_array *op_array)

Zend/zend_vm_def.h

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -8659,15 +8659,8 @@ ZEND_VM_HANDLER(183, ZEND_BIND_STATIC, CV, UNUSED, REF)
86598659

86608660
ht = ZEND_MAP_PTR_GET(EX(func)->op_array.static_variables_ptr);
86618661
if (!ht) {
8662-
ZEND_ASSERT(EX(func)->op_array.fn_flags & (ZEND_ACC_IMMUTABLE|ZEND_ACC_PRELOADED));
86638662
ht = zend_array_dup(EX(func)->op_array.static_variables);
86648663
ZEND_MAP_PTR_SET(EX(func)->op_array.static_variables_ptr, ht);
8665-
} else if (GC_REFCOUNT(ht) > 1) {
8666-
if (!(GC_FLAGS(ht) & IS_ARRAY_IMMUTABLE)) {
8667-
GC_DELREF(ht);
8668-
}
8669-
ht = zend_array_dup(ht);
8670-
ZEND_MAP_PTR_SET(EX(func)->op_array.static_variables_ptr, ht);
86718664
}
86728665

86738666
value = (zval*)((char*)ht->arData + (opline->extended_value & ~(ZEND_BIND_REF|ZEND_BIND_IMPLICIT)));

Zend/zend_vm_execute.h

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -47468,15 +47468,8 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_BIND_STATIC_SPEC_CV_UNUSED_HAN
4746847468

4746947469
ht = ZEND_MAP_PTR_GET(EX(func)->op_array.static_variables_ptr);
4747047470
if (!ht) {
47471-
ZEND_ASSERT(EX(func)->op_array.fn_flags & (ZEND_ACC_IMMUTABLE|ZEND_ACC_PRELOADED));
4747247471
ht = zend_array_dup(EX(func)->op_array.static_variables);
4747347472
ZEND_MAP_PTR_SET(EX(func)->op_array.static_variables_ptr, ht);
47474-
} else if (GC_REFCOUNT(ht) > 1) {
47475-
if (!(GC_FLAGS(ht) & IS_ARRAY_IMMUTABLE)) {
47476-
GC_DELREF(ht);
47477-
}
47478-
ht = zend_array_dup(ht);
47479-
ZEND_MAP_PTR_SET(EX(func)->op_array.static_variables_ptr, ht);
4748047473
}
4748147474

4748247475
value = (zval*)((char*)ht->arData + (opline->extended_value & ~(ZEND_BIND_REF|ZEND_BIND_IMPLICIT)));

0 commit comments

Comments
 (0)