Skip to content

Commit 9a1da9f

Browse files
committed
Don't use separate static variables in inherited methods
RFC: https://wiki.php.net/rfc/static_variable_inheritance Closes GH-6719.
1 parent 3e6b447 commit 9a1da9f

8 files changed

+74
-44
lines changed

UPGRADING

Lines changed: 11 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -37,28 +37,26 @@ PHP 8.1 UPGRADE NOTES
3737
// is deprecated
3838

3939
RFC: https://wiki.php.net/rfc/deprecate_null_to_scalar_internal_arg
40-
. When a method using static variables is inherited, the inherited method
41-
will now initialize the static variables to their original values, rather
42-
than the values at the time of inheritance:
40+
. When a method using static variables is inherited (but not overridden), the
41+
inherited method will now share static variables with the parent method.
4342

4443
class A {
45-
public function counter() {
44+
public static function counter() {
4645
static $counter = 0;
4746
$counter++;
4847
return $counter;
4948
}
5049
}
50+
class B extends A {}
5151

52-
var_dump((new A)->counter()); // int(1)
52+
var_dump(A::counter()); // int(1)
53+
var_dump(A::counter()); // int(2)
54+
var_dump(B::counter()); // int(3), previously int(1)
55+
var_dump(B::counter()); // int(4), previously int(2)
5356

54-
eval('class B extends A {}'); // eval() to prevent early binding.
55-
56-
var_dump((new B)->counter()); // int(1), previously int(2)
57-
var_dump((new A)->counter()); // int(2)
58-
var_dump((new B)->counter()); // int(2), previously int(3)
59-
60-
Previously the behavior would be different depending on whether A::counter()
61-
was called before class B was declared, or after it was declared.
57+
This means that static variables in methods now behave the same way as
58+
static properties.
59+
RFC: https://wiki.php.net/rfc/static_variable_inheritance
6260

6361
- Fileinfo:
6462
. The fileinfo functions now accept and return, respectively, finfo objects

Zend/tests/anon/015.phpt

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,12 +19,15 @@ var_dump($d->foo(24));
1919
var_dump($c->foo());
2020
?>
2121
--EXPECT--
22-
NULL
22+
array(1) {
23+
[0]=>
24+
int(42)
25+
}
2326
array(1) {
2427
[0]=>
2528
int(24)
2629
}
2730
array(1) {
2831
[0]=>
29-
int(42)
32+
int(24)
3033
}

Zend/tests/anon/016.phpt

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,13 +19,16 @@ var_dump($d->foo(24));
1919
var_dump($c->foo());
2020
?>
2121
--EXPECT--
22-
NULL
22+
array(1) {
23+
[0]=>
24+
object(stdClass)#2 (0) {
25+
}
26+
}
2327
array(1) {
2428
[0]=>
2529
int(24)
2630
}
2731
array(1) {
2832
[0]=>
29-
object(stdClass)#2 (0) {
30-
}
33+
int(24)
3134
}

Zend/tests/method_static_var.phpt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,5 +20,5 @@ Bar::test();
2020
--EXPECT--
2121
int(1)
2222
int(2)
23-
int(1)
24-
int(2)
23+
int(3)
24+
int(4)
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
--TEST--
2+
Behavior of static variables in trait methods
3+
--FILE--
4+
<?php
5+
6+
trait T {
7+
public static function counter() {
8+
static $i = 0;
9+
var_dump(++$i);
10+
}
11+
}
12+
13+
class C1 {
14+
use T {
15+
T::counter as counter1;
16+
T::counter as counter2;
17+
}
18+
}
19+
20+
class C2 {
21+
use T;
22+
}
23+
24+
C1::counter();
25+
C1::counter1();
26+
C1::counter2();
27+
C2::counter();
28+
29+
C1::counter();
30+
C1::counter1();
31+
C1::counter2();
32+
C2::counter();
33+
34+
?>
35+
--EXPECT--
36+
int(1)
37+
int(1)
38+
int(1)
39+
int(1)
40+
int(2)
41+
int(2)
42+
int(2)
43+
int(2)

Zend/zend_inheritance.c

Lines changed: 4 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -88,16 +88,7 @@ static zend_function *zend_duplicate_internal_function(zend_function *func, zend
8888
}
8989
/* }}} */
9090

91-
static zend_function *zend_duplicate_user_function(zend_function *func) /* {{{ */
92-
{
93-
zend_op_array *new_op_array = zend_arena_alloc(&CG(arena), sizeof(zend_op_array));
94-
memcpy(new_op_array, func, sizeof(zend_op_array));
95-
zend_init_static_variables_map_ptr(new_op_array);
96-
return (zend_function *) new_op_array;
97-
}
98-
/* }}} */
99-
100-
static zend_always_inline zend_function *zend_duplicate_function(zend_function *func, zend_class_entry *ce, bool is_interface) /* {{{ */
91+
static zend_always_inline zend_function *zend_duplicate_function(zend_function *func, zend_class_entry *ce) /* {{{ */
10192
{
10293
if (UNEXPECTED(func->type == ZEND_INTERNAL_FUNCTION)) {
10394
return zend_duplicate_internal_function(func, ce);
@@ -108,12 +99,7 @@ static zend_always_inline zend_function *zend_duplicate_function(zend_function *
10899
if (EXPECTED(func->op_array.function_name)) {
109100
zend_string_addref(func->op_array.function_name);
110101
}
111-
if (is_interface
112-
|| EXPECTED(!func->op_array.static_variables)) {
113-
/* reuse the same op_array structure */
114-
return func;
115-
}
116-
return zend_duplicate_user_function(func);
102+
return func;
117103
}
118104
}
119105
/* }}} */
@@ -948,9 +934,7 @@ static zend_always_inline inheritance_status do_inheritance_check_on_method_ex(
948934

949935
if (!check_only && child->common.prototype != proto && child_zv) {
950936
do {
951-
if (child->common.scope != ce
952-
&& child->type == ZEND_USER_FUNCTION
953-
&& !child->op_array.static_variables) {
937+
if (child->common.scope != ce && child->type == ZEND_USER_FUNCTION) {
954938
if (ce->ce_flags & ZEND_ACC_INTERFACE) {
955939
/* Few parent interfaces contain the same method */
956940
break;
@@ -1021,7 +1005,7 @@ static zend_always_inline void do_inherit_method(zend_string *key, zend_function
10211005
ce->ce_flags |= ZEND_ACC_IMPLICIT_ABSTRACT_CLASS;
10221006
}
10231007

1024-
parent = zend_duplicate_function(parent, ce, is_interface);
1008+
parent = zend_duplicate_function(parent, ce);
10251009

10261010
if (!is_interface) {
10271011
_zend_hash_append_ptr(&ce->function_table, key, parent);

ext/opcache/tests/preload_method_static_vars.phpt

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,8 @@ Bar::test();
1919
--EXPECT--
2020
int(1)
2121
int(2)
22-
int(1)
23-
int(2)
22+
int(3)
23+
int(4)
2424

2525
int(1)
26-
int(1)
26+
int(2)

ext/opcache/zend_persist.c

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -763,7 +763,6 @@ static void zend_persist_class_method(zval *zv, zend_class_entry *ce)
763763
}
764764

765765
if ((op_array->fn_flags & ZEND_ACC_IMMUTABLE)
766-
&& !op_array->static_variables
767766
&& !ZCG(current_persistent_script)->corrupted
768767
&& zend_accel_in_shm(op_array)) {
769768
zend_shared_alloc_register_xlat_entry(op_array, op_array);

0 commit comments

Comments
 (0)