Skip to content

Commit 4344385

Browse files
committed
Add static return type
RFC: https://wiki.php.net/rfc/static_return_type The "static" type is represented as MAY_BE_STATIC, rather than a class type like "self" and "parent", as it has special resolution semantics, and cannot be cached in the runtime cache. Closes GH-5062.
1 parent d2ba848 commit 4344385

22 files changed

+400
-26
lines changed

UPGRADING

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -367,6 +367,15 @@ PHP 8.0 UPGRADE NOTES
367367
class B extends A {
368368
public function method(...$everything) {}
369369
}
370+
. "static" (as in "late static binding") can now be used as a return type:
371+
372+
class Test {
373+
public function create(): static {
374+
return new static();
375+
}
376+
}
377+
378+
RFC: https://wiki.php.net/rfc/static_return_type
370379
. It is now possible to fetch the class name of an object using
371380
`$object::class`. The result is the same as `get_class($object)`.
372381
RFC: https://wiki.php.net/rfc/class_name_literal_on_object
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
--TEST--
2+
While it may not be very useful, static is also permitted in final classes
3+
--FILE--
4+
<?php
5+
6+
final class Test {
7+
public static function create(): static {
8+
return new static;
9+
}
10+
}
11+
12+
var_dump(Test::create());
13+
14+
?>
15+
--EXPECT--
16+
object(Test)#1 (0) {
17+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
--TEST--
2+
Static type outside class generates compile error
3+
--FILE--
4+
<?php
5+
6+
function test(): static {}
7+
8+
?>
9+
--EXPECTF--
10+
Fatal error: Cannot use "static" when no class scope is active in %s on line %d
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
--TEST--
2+
Static type is not allowed in parameters
3+
--FILE--
4+
<?php
5+
6+
// TODO: We could prohibit this case in the compiler instead.
7+
8+
class Test {
9+
public function test(static $param) {
10+
}
11+
}
12+
13+
?>
14+
--EXPECTF--
15+
Parse error: syntax error, unexpected 'static' (T_STATIC), expecting variable (T_VARIABLE) in %s on line %d
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
--TEST--
2+
Static type is not allowed in properties
3+
--FILE--
4+
<?php
5+
6+
// Testing ?static here, to avoid ambiguity with static properties.
7+
class Test {
8+
public ?static $foo;
9+
}
10+
11+
?>
12+
--EXPECTF--
13+
Parse error: syntax error, unexpected 'static' (T_STATIC) in %s on line %d
Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
--TEST--
2+
Static return type
3+
--FILE--
4+
<?php
5+
6+
class A {
7+
public function test1(): static {
8+
return new static;
9+
}
10+
public function test2(): static {
11+
return new self;
12+
}
13+
public function test3(): static {
14+
return new static;
15+
}
16+
public function test4(): static|array {
17+
return new self;
18+
}
19+
}
20+
21+
class B extends A {
22+
public function test3(): static {
23+
return new C;
24+
}
25+
}
26+
27+
class C extends B {}
28+
29+
$a = new A;
30+
$b = new B;
31+
32+
var_dump($a->test1());
33+
var_dump($b->test1());
34+
35+
echo "\n";
36+
var_dump($a->test2());
37+
try {
38+
var_dump($b->test2());
39+
} catch (TypeError $e) {
40+
echo $e->getMessage(), "\n";
41+
}
42+
43+
echo "\n";
44+
var_dump($a->test3());
45+
var_dump($b->test3());
46+
47+
echo "\n";
48+
var_dump($a->test4());
49+
try {
50+
var_dump($b->test4());
51+
} catch (TypeError $e) {
52+
echo $e->getMessage(), "\n";
53+
}
54+
55+
echo "\n";
56+
$test = function($x): static {
57+
return $x;
58+
};
59+
60+
try {
61+
var_dump($test(new stdClass));
62+
} catch (TypeError $e) {
63+
echo $e->getMessage(), "\n";
64+
}
65+
66+
$test = $test->bindTo($a);
67+
var_dump($test($a));
68+
69+
?>
70+
--EXPECT--
71+
object(A)#3 (0) {
72+
}
73+
object(B)#3 (0) {
74+
}
75+
76+
object(A)#3 (0) {
77+
}
78+
Return value of A::test2() must be an instance of B, instance of A returned
79+
80+
object(A)#3 (0) {
81+
}
82+
object(C)#3 (0) {
83+
}
84+
85+
object(A)#3 (0) {
86+
}
87+
Return value of A::test4() must be of type B|array, instance of A returned
88+
89+
Return value of {closure}() must be an instance of static, instance of stdClass returned
90+
object(A)#1 (0) {
91+
}
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
--TEST--
2+
static type in trait
3+
--FILE--
4+
<?php
5+
6+
trait T {
7+
public function test($arg): static {
8+
return $arg;
9+
}
10+
}
11+
12+
class C {
13+
use T;
14+
}
15+
class P extends C {
16+
}
17+
18+
$c = new C;
19+
$p = new P;
20+
var_dump($c->test($c));
21+
var_dump($c->test($p));
22+
var_dump($p->test($p));
23+
var_dump($p->test($c));
24+
25+
?>
26+
--EXPECTF--
27+
object(C)#1 (0) {
28+
}
29+
object(P)#2 (0) {
30+
}
31+
object(P)#2 (0) {
32+
}
33+
34+
Fatal error: Uncaught TypeError: Return value of C::test() must be an instance of P, instance of C returned in %s:%d
35+
Stack trace:
36+
#0 %s(%d): C->test(Object(C))
37+
#1 {main}
38+
thrown in %s on line %d
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
--TEST--
2+
object and static are redundant
3+
--FILE--
4+
<?php
5+
6+
class Test {
7+
public function foo(): static|object {}
8+
}
9+
10+
?>
11+
--EXPECTF--
12+
Fatal error: Type static|object contains both object and a class type, which is redundant in %s on line %d
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
--TEST--
2+
Failure case for static variance: Replace static with self
3+
--FILE--
4+
<?php
5+
6+
class A {
7+
public function test(): static {}
8+
}
9+
class B extends A {
10+
public function test(): self {}
11+
}
12+
13+
?>
14+
--EXPECTF--
15+
Fatal error: Declaration of B::test(): B must be compatible with A::test(): static in %s on line %d
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
--TEST--
2+
Success cases for static variance
3+
--FILE--
4+
<?php
5+
6+
class A {
7+
public function test1(): self {}
8+
public function test2(): B {}
9+
public function test3(): object {}
10+
public function test4(): X|Y|self {}
11+
public function test5(): ?static {}
12+
}
13+
14+
class B extends A {
15+
public function test1(): static {}
16+
public function test2(): static {}
17+
public function test3(): static {}
18+
public function test4(): X|Y|static {}
19+
public function test5(): static {}
20+
}
21+
22+
?>
23+
===DONE===
24+
--EXPECT--
25+
===DONE===

0 commit comments

Comments
 (0)