Skip to content

Commit 8365f5c

Browse files
committed
Infer static for closures and add stateless closure cache
See #19754 (comment)
1 parent 80b1ede commit 8365f5c

26 files changed

Lines changed: 271 additions & 133 deletions

Zend/Optimizer/compact_literals.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -742,6 +742,7 @@ void zend_optimizer_compact_literals(zend_op_array *op_array, zend_optimizer_ctx
742742
}
743743
break;
744744
case ZEND_CALLABLE_CONVERT:
745+
case ZEND_DECLARE_LAMBDA_FUNCTION:
745746
if (opline->extended_value != (uint32_t)-1) {
746747
opline->extended_value = cache_size;
747748
cache_size += sizeof(void *);

Zend/tests/arrow_functions/005.phpt

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,8 +40,7 @@ class Test {
4040

4141
?>
4242
--EXPECT--
43-
object(Test)#1 (0) {
44-
}
43+
NULL
4544
object(Test)#1 (0) {
4645
}
4746
object(Test)#1 (0) {

Zend/tests/bug75079_2.phpt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,6 @@ int(123)
3131

3232
Fatal error: Uncaught Error: Cannot access private property Foo::$bar in %s:%d
3333
Stack trace:
34-
#0 %s(%d): A->{closure:%s:%d}()
34+
#0 %s(%d): A::{closure:%s:%d}()
3535
#1 {main}
3636
thrown in %s on line %d

Zend/tests/closures/closure_020.phpt

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ object(foo)#%d (2) {
2828
["test":"foo":private]=>
2929
int(3)
3030
["a"]=>
31-
object(Closure)#%d (5) {
31+
object(Closure)#%d (%d) {
3232
["name"]=>
3333
string(%d) "{closure:%s:%d}"
3434
["file"]=>
@@ -40,8 +40,6 @@ object(foo)#%d (2) {
4040
["a"]=>
4141
*RECURSION*
4242
}
43-
["this"]=>
44-
*RECURSION*
4543
}
4644
}
4745
bool(true)

Zend/tests/closures/closure_026.phpt

Lines changed: 2 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -33,15 +33,13 @@ object(foo)#%d (1) {
3333
["a"]=>
3434
array(1) {
3535
[0]=>
36-
object(Closure)#%d (4) {
36+
object(Closure)#%d (%d) {
3737
["name"]=>
3838
string(%d) "{closure:%s:%d}"
3939
["file"]=>
4040
string(%d) "%s"
4141
["line"]=>
4242
int(%d)
43-
["this"]=>
44-
*RECURSION*
4543
}
4644
}
4745
}
@@ -50,18 +48,13 @@ int(1)
5048
string(1) "a"
5149
array(1) {
5250
[0]=>
53-
object(Closure)#%d (4) {
51+
object(Closure)#%d (%d) {
5452
["name"]=>
5553
string(%d) "{closure:%s:%d}"
5654
["file"]=>
5755
string(%d) "%s"
5856
["line"]=>
5957
int(%d)
60-
["this"]=>
61-
object(foo)#%d (1) {
62-
["a"]=>
63-
*RECURSION*
64-
}
6558
}
6659
}
6760
int(1)

Zend/tests/gc/bug60139.phpt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ class Foo {
88
public $x;
99

1010
public function __construct() {
11-
$this->x = function() {};
11+
$this->x = function () { $this; };
1212
}
1313
}
1414

@@ -17,7 +17,7 @@ class Bar {
1717

1818
public function __construct() {
1919
$self = $this;
20-
$this->x = function() use ($self) {};
20+
$this->x = function() use ($self) { $this; };
2121
}
2222
}
2323

Zend/tests/generators/generator_closure_collection.phpt

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ The Closure object of a generator is freed when the generator is freed.
44
<?php
55

66
$genFactory = function() {
7+
/* Avoid static inference and caching. */
8+
static $dummy;
79
yield 1;
810
yield 2;
911
yield 3;
@@ -25,23 +27,33 @@ var_dump($r->get());
2527

2628
?>
2729
--EXPECTF--
28-
object(Closure)#%d (3) {
30+
object(Closure)#%d (4) {
2931
["name"]=>
3032
string(%d) "{closure:%s:%d}"
3133
["file"]=>
3234
string(%d) "%s"
3335
["line"]=>
3436
int(%d)
37+
["static"]=>
38+
array(1) {
39+
["dummy"]=>
40+
NULL
41+
}
3542
}
3643
int(1)
3744
int(2)
3845
int(3)
39-
object(Closure)#%d (3) {
46+
object(Closure)#%d (4) {
4047
["name"]=>
4148
string(%d) "{closure:%s:%d}"
4249
["file"]=>
4350
string(%d) "%s"
4451
["line"]=>
4552
int(%d)
53+
["static"]=>
54+
array(1) {
55+
["dummy"]=>
56+
NULL
57+
}
4658
}
4759
NULL

Zend/tests/return_types/012.phpt

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ $baz = new foo();
1515
var_dump($baz->bar());
1616
?>
1717
--EXPECTF--
18-
object(Closure)#%d (5) {
18+
object(Closure)#%d (4) {
1919
["name"]=>
2020
string(%d) "{closure:%s:%d}"
2121
["file"]=>
@@ -27,7 +27,4 @@ object(Closure)#%d (5) {
2727
["test"]=>
2828
string(3) "one"
2929
}
30-
["this"]=>
31-
object(foo)#%d (0) {
32-
}
3330
}

Zend/tests/return_types/013.phpt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,6 @@ var_dump($func=$baz->bar(), $func());
1717
--EXPECTF--
1818
Fatal error: Uncaught TypeError: foo::{closure:%s:%d}(): Return value must be of type array, null returned in %s:%d
1919
Stack trace:
20-
#0 %s(%d): foo->{closure:%s:%d}()
20+
#0 %s(%d): foo::{closure:%s:%d}()
2121
#1 {main}
2222
thrown in %s on line %d

Zend/zend_closures.c

Lines changed: 16 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -75,14 +75,19 @@ ZEND_METHOD(Closure, __invoke) /* {{{ */
7575
/* }}} */
7676

7777
static bool zend_valid_closure_binding(
78-
zend_closure *closure, zval *newthis, zend_class_entry *scope) /* {{{ */
78+
zend_closure *closure, zval **newthis_ptr, zend_class_entry *scope) /* {{{ */
7979
{
80+
zval *newthis = *newthis_ptr;
8081
zend_function *func = &closure->func;
8182
bool is_fake_closure = (func->common.fn_flags & ZEND_ACC_FAKE_CLOSURE) != 0;
8283
if (newthis) {
8384
if (func->common.fn_flags & ZEND_ACC_STATIC) {
84-
zend_error(E_WARNING, "Cannot bind an instance to a static closure, this will be an error in PHP 9");
85-
return false;
85+
if (!(func->common.fn_flags2 & ZEND_ACC2_INFERRED_STATIC)) {
86+
zend_error(E_WARNING, "Cannot bind an instance to a static closure, this will be an error in PHP 9");
87+
return false;
88+
} else {
89+
*newthis_ptr = NULL;
90+
}
8691
}
8792

8893
if (is_fake_closure && func->common.scope &&
@@ -144,13 +149,14 @@ ZEND_METHOD(Closure, call)
144149

145150
closure = (zend_closure *) Z_OBJ_P(ZEND_THIS);
146151

147-
newobj = Z_OBJ_P(newthis);
148-
newclass = newobj->ce;
152+
newclass = Z_OBJ_P(newthis)->ce;
149153

150-
if (!zend_valid_closure_binding(closure, newthis, newclass)) {
154+
if (!zend_valid_closure_binding(closure, &newthis, newclass)) {
151155
return;
152156
}
153157

158+
newobj = newthis ? Z_OBJ_P(newthis) : NULL;
159+
154160
fci_cache.called_scope = newclass;
155161
fci_cache.object = fci.object = newobj;
156162

@@ -241,16 +247,16 @@ static void do_closure_bind(zval *return_value, zval *zclosure, zval *newthis, z
241247
ce = NULL;
242248
}
243249

244-
if (!zend_valid_closure_binding(closure, newthis, ce)) {
245-
return;
246-
}
247-
248250
if (newthis) {
249251
called_scope = Z_OBJCE_P(newthis);
250252
} else {
251253
called_scope = ce;
252254
}
253255

256+
if (!zend_valid_closure_binding(closure, &newthis, ce)) {
257+
return;
258+
}
259+
254260
zend_create_closure(return_value, &closure->func, ce, called_scope, newthis);
255261
}
256262

0 commit comments

Comments
 (0)