Skip to content

Typed class constants #10444

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 11 commits into from
Apr 16, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions UPGRADING
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,8 @@ PHP 8.3 UPGRADE NOTES
. Anonymous classes may now be marked as readonly.
. Readonly properties can now be reinitialized during cloning.
RFC: https://wiki.php.net/rfc/readonly_amendments
. Class, interface, trait, and enum constants now support type
declarations. RFC: https://wiki.php.net/rfc/typed_class_constants

- Posix
. posix_getrlimit() now takes an optional $res parameter to allow fetching a
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
--TEST--
Typed class constants (diamond error with self)
--FILE--
<?php
class A {
public const self CONST1 = C;
}

try {
define("C", new A());
} catch (Error $exception) {
echo $exception->getMessage() . "\n";
}
?>
--EXPECT--
Undefined constant "C"
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
--TEST--
Typed class constants (incompatible inheritance; simple)
--FILE--
<?php
class A {
public const int CONST1 = 1;
}

class B extends A {
public const string CONST1 = 'a';
}
?>
--EXPECTF--
Fatal error: Type of B::CONST1 must be compatible with A::CONST1 of type int in %s on line %d
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
--TEST--
Typed class constants (incompatible inheritance; missing type in child)
--FILE--
<?php
class A {
public const int CONST1 = 1;
}

class B extends A {
public const CONST1 = 0;
}
?>
--EXPECTF--
Fatal error: Type of B::CONST1 must be compatible with A::CONST1 of type int in %s on line %d
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
--TEST--
Typed class constants (incompatible composition; traits)
--FILE--
<?php

trait T {
public const ?array CONST1 = [];
}

class C {
use T;

public const CONST1 = [];
}

?>
--EXPECTF--
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
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
--TEST--
Typed class constants (incompatible covariant composition; traits)
--FILE--
<?php

trait T {
public const ?array CONST1 = [];
}

class C {
use T;

public const array CONST1 = [];
}

?>
--EXPECTF--
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
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
--TEST--
Typed class constants (incompatible contravariant composition; traits)
--FILE--
<?php

trait T {
public const array CONST1 = [];
}

class C {
use T;

public const ?array CONST1 = [];
}

?>
--EXPECTF--
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
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
--TEST--
Typed class constants (inheritance success)
--FILE--
<?php
class A {
public const CONST1 = 1;
public const CONST2 = 1;
public const mixed CONST3 = 1;
public const iterable CONST4 = [];
}

class B extends A {
public const int CONST1 = 0;
public const mixed CONST2 = 0;
public const mixed CONST3 = 0;
public const array CONST4 = [];
}

var_dump(B::CONST1);
var_dump(B::CONST2);
var_dump(B::CONST3);
var_dump(B::CONST4);
?>
--EXPECT--
int(0)
int(0)
int(0)
array(0) {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
--TEST--
Typed class constants (inheritance success - object types)
--FILE--
<?php
class S implements Stringable {
public function __toString() {
return "";
}
}

class Z extends S {}

class A {
public const object CONST1 = S;
public const S CONST2 = S;
public const S|Stringable CONST3 = S;
public const S CONST4 = S;
public const ?S CONST5 = S;
}

class B extends A {
public const S CONST1 = Z;
public const Z CONST2 = Z;
public const S CONST3 = Z;
public const S&Stringable CONST4 = Z;
public const (S&Stringable)|null CONST5 = Z;
}

define("S", new S());
define("Z", new Z());

var_dump(B::CONST1);
var_dump(B::CONST2);
var_dump(B::CONST3);
var_dump(B::CONST4);
var_dump(B::CONST5);
?>
--EXPECTF--
object(Z)#%d (%d) {
}
object(Z)#%d (%d) {
}
object(Z)#%d (%d) {
}
object(Z)#%d (%d) {
}
object(Z)#%d (%d) {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
--TEST--
Typed class constants (inheritance; private constants)
--FILE--
<?php
class A {
private const int CONST1 = 1;
}

class B extends A {
public const string CONST1 = 'a';
}

var_dump(B::CONST1);
?>
--EXPECT--
string(1) "a"
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
--TEST--
Typed class constants (composition; traits)
--FILE--
<?php

const G = new stdClass();

enum E {
case Case1;
}

trait T {
public const int CONST1 = 1;
public const ?array CONST2 = [];
public const E CONST3 = E::Case1;
public const stdClass CONST4 = G;
}

class C {
use T;

public const int CONST1 = 1;
public const ?array CONST2 = [];
public const E CONST3 = E::Case1;
public const stdClass CONST4 = G;
}

var_dump(C::CONST1);
var_dump(C::CONST2);
var_dump(C::CONST3);
var_dump(C::CONST4);
?>
--EXPECT--
int(1)
array(0) {
}
enum(E::Case1)
object(stdClass)#1 (0) {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
--TEST--
Typed class constants (redefinition; interfaces and traits)
--FILE--
<?php
enum E {
case Case1;
}

interface I {
public const E CONST1 = E::Case1;
}

trait T {
public const E CONST1 = E::Case1;
}

class C implements I {
use T;

public const E CONST1 = E::Case1;
}

var_dump(C::CONST1);
?>
--EXPECT--
enum(E::Case1)
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
--TEST--
Typed class constants (type mismatch; simple)
--FILE--
<?php
class A {
public const string CONST1 = 1;
}
?>
--EXPECTF--
Fatal error: Cannot use int as value for class constant A::CONST1 of type string in %s on line %d
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
--TEST--
Typed class constants (type error)
--FILE--
<?php
class A1 {
const ?B1 C = null;
}

class A2 extends A1 {
const ?B2 C = null;
}

?>
--EXPECTF--
Fatal error: Type of A2::C must be compatible with A1::C of type ?B1 in %s on line %d
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
--TEST--
Typed class constants (static type error)
--FILE--
<?php
enum E1 {
const static C = E2::Foo;
}

enum E2 {
case Foo;
}

try {
var_dump(E1::C);
} catch (TypeError $e) {
echo $e->getMessage() . "\n";
}

?>
--EXPECT--
Cannot assign E2 to class constant E1::C of type static
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
--TEST--
Typed class constants with static in union
--FILE--
<?php
class A {
public const static|B CONST1 = B;
public const static|stdClass|B CONST2 = B;
}
class B {}
define("B", new B());
var_dump(new A());
?>
--EXPECT--
object(A)#2 (0) {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
--TEST--
Typed class constants (type mismatch; runtime simple)
--FILE--
<?php
class A {
public const int CONST1 = C;
}

define("C", "c");

try {
var_dump(A::CONST1);
} catch (TypeError $exception) {
echo $exception->getMessage() . "\n";
}

try {
var_dump(A::CONST1);
} catch (TypeError $exception) {
echo $exception->getMessage() . "\n";
}

?>
--EXPECT--
Cannot assign string to class constant A::CONST1 of type int
Cannot assign string to class constant A::CONST1 of type int
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
--TEST--
Typed class constants (type mismatch; runtime object)
--FILE--
<?php
class A {
public const string CONST1 = C;
}

define('C', new stdClass);

try {
var_dump(A::CONST1);
} catch (TypeError $exception) {
echo $exception->getMessage() . "\n";
}

try {
var_dump(A::CONST1);
} catch (TypeError $exception) {
echo $exception->getMessage() . "\n";
}
?>
--EXPECT--
Cannot assign stdClass to class constant A::CONST1 of type string
Cannot assign stdClass to class constant A::CONST1 of type string
Loading