Skip to content

Commit 414f71a

Browse files
kocsismatemoliatabwoebiiluuu1994
authored
Typed class constants (#10444)
RFC: https://wiki.php.net/rfc/typed_class_constants Co-Authored-By: Ben <[email protected]> Co-Authored-By: Bob Weinand <[email protected]> Co-Authored-By: Ilija Tovilo <[email protected]>
1 parent 4dad419 commit 414f71a

File tree

53 files changed

+1227
-92
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

53 files changed

+1227
-92
lines changed

UPGRADING

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,8 @@ PHP 8.3 UPGRADE NOTES
5252
. Anonymous classes may now be marked as readonly.
5353
. Readonly properties can now be reinitialized during cloning.
5454
RFC: https://wiki.php.net/rfc/readonly_amendments
55+
. Class, interface, trait, and enum constants now support type
56+
declarations. RFC: https://wiki.php.net/rfc/typed_class_constants
5557

5658
- Posix
5759
. posix_getrlimit() now takes an optional $res parameter to allow fetching a
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
--TEST--
2+
Typed class constants (diamond error with self)
3+
--FILE--
4+
<?php
5+
class A {
6+
public const self CONST1 = C;
7+
}
8+
9+
try {
10+
define("C", new A());
11+
} catch (Error $exception) {
12+
echo $exception->getMessage() . "\n";
13+
}
14+
?>
15+
--EXPECT--
16+
Undefined constant "C"
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
--TEST--
2+
Typed class constants (incompatible inheritance; simple)
3+
--FILE--
4+
<?php
5+
class A {
6+
public const int CONST1 = 1;
7+
}
8+
9+
class B extends A {
10+
public const string CONST1 = 'a';
11+
}
12+
?>
13+
--EXPECTF--
14+
Fatal error: Type of B::CONST1 must be compatible with A::CONST1 of type int in %s on line %d
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
--TEST--
2+
Typed class constants (incompatible inheritance; missing type in child)
3+
--FILE--
4+
<?php
5+
class A {
6+
public const int CONST1 = 1;
7+
}
8+
9+
class B extends A {
10+
public const CONST1 = 0;
11+
}
12+
?>
13+
--EXPECTF--
14+
Fatal error: Type of B::CONST1 must be compatible with A::CONST1 of type int in %s on line %d
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
--TEST--
2+
Typed class constants (incompatible composition; traits)
3+
--FILE--
4+
<?php
5+
6+
trait T {
7+
public const ?array CONST1 = [];
8+
}
9+
10+
class C {
11+
use T;
12+
13+
public const CONST1 = [];
14+
}
15+
16+
?>
17+
--EXPECTF--
18+
Fatal error: C and T define the same constant (CONST1) in the composition of C. However, the definition differs and is considered incompatible. Class was composed in %s on line %d
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
--TEST--
2+
Typed class constants (incompatible covariant composition; traits)
3+
--FILE--
4+
<?php
5+
6+
trait T {
7+
public const ?array CONST1 = [];
8+
}
9+
10+
class C {
11+
use T;
12+
13+
public const array CONST1 = [];
14+
}
15+
16+
?>
17+
--EXPECTF--
18+
Fatal error: C and T define the same constant (CONST1) in the composition of C. However, the definition differs and is considered incompatible. Class was composed in %s on line %d
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
--TEST--
2+
Typed class constants (incompatible contravariant composition; traits)
3+
--FILE--
4+
<?php
5+
6+
trait T {
7+
public const array CONST1 = [];
8+
}
9+
10+
class C {
11+
use T;
12+
13+
public const ?array CONST1 = [];
14+
}
15+
16+
?>
17+
--EXPECTF--
18+
Fatal error: C and T define the same constant (CONST1) in the composition of C. However, the definition differs and is considered incompatible. Class was composed in %s on line %d
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
--TEST--
2+
Typed class constants (inheritance success)
3+
--FILE--
4+
<?php
5+
class A {
6+
public const CONST1 = 1;
7+
public const CONST2 = 1;
8+
public const mixed CONST3 = 1;
9+
public const iterable CONST4 = [];
10+
}
11+
12+
class B extends A {
13+
public const int CONST1 = 0;
14+
public const mixed CONST2 = 0;
15+
public const mixed CONST3 = 0;
16+
public const array CONST4 = [];
17+
}
18+
19+
var_dump(B::CONST1);
20+
var_dump(B::CONST2);
21+
var_dump(B::CONST3);
22+
var_dump(B::CONST4);
23+
?>
24+
--EXPECT--
25+
int(0)
26+
int(0)
27+
int(0)
28+
array(0) {
29+
}
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
--TEST--
2+
Typed class constants (inheritance success - object types)
3+
--FILE--
4+
<?php
5+
class S implements Stringable {
6+
public function __toString() {
7+
return "";
8+
}
9+
}
10+
11+
class Z extends S {}
12+
13+
class A {
14+
public const object CONST1 = S;
15+
public const S CONST2 = S;
16+
public const S|Stringable CONST3 = S;
17+
public const S CONST4 = S;
18+
public const ?S CONST5 = S;
19+
}
20+
21+
class B extends A {
22+
public const S CONST1 = Z;
23+
public const Z CONST2 = Z;
24+
public const S CONST3 = Z;
25+
public const S&Stringable CONST4 = Z;
26+
public const (S&Stringable)|null CONST5 = Z;
27+
}
28+
29+
define("S", new S());
30+
define("Z", new Z());
31+
32+
var_dump(B::CONST1);
33+
var_dump(B::CONST2);
34+
var_dump(B::CONST3);
35+
var_dump(B::CONST4);
36+
var_dump(B::CONST5);
37+
?>
38+
--EXPECTF--
39+
object(Z)#%d (%d) {
40+
}
41+
object(Z)#%d (%d) {
42+
}
43+
object(Z)#%d (%d) {
44+
}
45+
object(Z)#%d (%d) {
46+
}
47+
object(Z)#%d (%d) {
48+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
--TEST--
2+
Typed class constants (inheritance; private constants)
3+
--FILE--
4+
<?php
5+
class A {
6+
private const int CONST1 = 1;
7+
}
8+
9+
class B extends A {
10+
public const string CONST1 = 'a';
11+
}
12+
13+
var_dump(B::CONST1);
14+
?>
15+
--EXPECT--
16+
string(1) "a"
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
--TEST--
2+
Typed class constants (composition; traits)
3+
--FILE--
4+
<?php
5+
6+
const G = new stdClass();
7+
8+
enum E {
9+
case Case1;
10+
}
11+
12+
trait T {
13+
public const int CONST1 = 1;
14+
public const ?array CONST2 = [];
15+
public const E CONST3 = E::Case1;
16+
public const stdClass CONST4 = G;
17+
}
18+
19+
class C {
20+
use T;
21+
22+
public const int CONST1 = 1;
23+
public const ?array CONST2 = [];
24+
public const E CONST3 = E::Case1;
25+
public const stdClass CONST4 = G;
26+
}
27+
28+
var_dump(C::CONST1);
29+
var_dump(C::CONST2);
30+
var_dump(C::CONST3);
31+
var_dump(C::CONST4);
32+
?>
33+
--EXPECT--
34+
int(1)
35+
array(0) {
36+
}
37+
enum(E::Case1)
38+
object(stdClass)#1 (0) {
39+
}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
--TEST--
2+
Typed class constants (redefinition; interfaces and traits)
3+
--FILE--
4+
<?php
5+
enum E {
6+
case Case1;
7+
}
8+
9+
interface I {
10+
public const E CONST1 = E::Case1;
11+
}
12+
13+
trait T {
14+
public const E CONST1 = E::Case1;
15+
}
16+
17+
class C implements I {
18+
use T;
19+
20+
public const E CONST1 = E::Case1;
21+
}
22+
23+
var_dump(C::CONST1);
24+
?>
25+
--EXPECT--
26+
enum(E::Case1)
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
--TEST--
2+
Typed class constants (type mismatch; simple)
3+
--FILE--
4+
<?php
5+
class A {
6+
public const string CONST1 = 1;
7+
}
8+
?>
9+
--EXPECTF--
10+
Fatal error: Cannot use int as value for class constant A::CONST1 of type string 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+
Typed class constants (type error)
3+
--FILE--
4+
<?php
5+
class A1 {
6+
const ?B1 C = null;
7+
}
8+
9+
class A2 extends A1 {
10+
const ?B2 C = null;
11+
}
12+
13+
?>
14+
--EXPECTF--
15+
Fatal error: Type of A2::C must be compatible with A1::C of type ?B1 in %s on line %d
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
--TEST--
2+
Typed class constants (static type error)
3+
--FILE--
4+
<?php
5+
enum E1 {
6+
const static C = E2::Foo;
7+
}
8+
9+
enum E2 {
10+
case Foo;
11+
}
12+
13+
try {
14+
var_dump(E1::C);
15+
} catch (TypeError $e) {
16+
echo $e->getMessage() . "\n";
17+
}
18+
19+
?>
20+
--EXPECT--
21+
Cannot assign E2 to class constant E1::C of type static
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
--TEST--
2+
Typed class constants with static in union
3+
--FILE--
4+
<?php
5+
class A {
6+
public const static|B CONST1 = B;
7+
public const static|stdClass|B CONST2 = B;
8+
}
9+
class B {}
10+
define("B", new B());
11+
var_dump(new A());
12+
?>
13+
--EXPECT--
14+
object(A)#2 (0) {
15+
}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
--TEST--
2+
Typed class constants (type mismatch; runtime simple)
3+
--FILE--
4+
<?php
5+
class A {
6+
public const int CONST1 = C;
7+
}
8+
9+
define("C", "c");
10+
11+
try {
12+
var_dump(A::CONST1);
13+
} catch (TypeError $exception) {
14+
echo $exception->getMessage() . "\n";
15+
}
16+
17+
try {
18+
var_dump(A::CONST1);
19+
} catch (TypeError $exception) {
20+
echo $exception->getMessage() . "\n";
21+
}
22+
23+
?>
24+
--EXPECT--
25+
Cannot assign string to class constant A::CONST1 of type int
26+
Cannot assign string to class constant A::CONST1 of type int
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
--TEST--
2+
Typed class constants (type mismatch; runtime object)
3+
--FILE--
4+
<?php
5+
class A {
6+
public const string CONST1 = C;
7+
}
8+
9+
define('C', new stdClass);
10+
11+
try {
12+
var_dump(A::CONST1);
13+
} catch (TypeError $exception) {
14+
echo $exception->getMessage() . "\n";
15+
}
16+
17+
try {
18+
var_dump(A::CONST1);
19+
} catch (TypeError $exception) {
20+
echo $exception->getMessage() . "\n";
21+
}
22+
?>
23+
--EXPECT--
24+
Cannot assign stdClass to class constant A::CONST1 of type string
25+
Cannot assign stdClass to class constant A::CONST1 of type string

0 commit comments

Comments
 (0)