PHP 8.3 新特性 - Typed Class Constants
hi,我是温新,一名 PHPer
PHP 8.3 引入了一个新的特性 Typed Class Constants(类型提示类常量)
。类型提示类常量允许为类常量指定一个预
期的类型,这样在使用时就可以确保类型的正确性,有助于提高代码的可读性和健壮性。
PHP 8.3 及更高版本中,声明类常量类型,可以在 const
关键字声明类型,如下:
<?php
class Person {
public const int AGE = 18;
}
echo Person::AGE;
PHP 8.3 之前,是不支持为类常量声明类型的,否则会报错,如下:
<?php
// PHP 8.3 之前的版本
class Dog {
public const string NAME = "花花";
}
echo Dog::NAME;
报错如下:
PHP Parse error: syntax error, unexpected identifier "NAME", expecting "="
PHP 8.3 中,当类常量声明的类型与值类型不匹配时,会报致命错误,如下:
<?php
class Cat {
public const string NAME = 5;
}
报错如下:
PHP Fatal error: Cannot use int as value for class constant Cat::NAME of type string in
以下是使用常量可见性修饰符(PHP 7.1)、final 类常量(PHP 8.1)以及类型提示类常量(8.3)。此外,在枚举中声明的常量也支持添加类型。
具有类型提示类常量的类
<?php
class Person {
public const string NAME = '丽丽';
protected const int AGE = 19;
final protected const string GENDER = '女';
}
具有类型提示类常量的 Traits
<?php
class UserTrait {
public const string NAME = "美美";
}
具有类型揭示类常量的枚举
enum StatusEnum: string {
public const string DRAFT = '草稿';
}
支持的类型和类型强制转换
类常量支持标准的 PHP 独立类型、可空类型、联合类型、交集类型和 DNF 类型。
以下类型不支持作为类常量类型:
-
void
和never
类型:这些类型仅用于返回类型; -
callable
:这种类型是上下文相关的,也不支持在类型属性中使用。但允许使用 Closure 类型。
以下声明将会报错,案例如下:
<?php
class Person {
const void NAME = "丽丽";
const never AGE = 19;
const callable GENDER = "女";
}
报错如下:
PHP Fatal error: Class constant Person::NAME cannot have type void in
PHP Fatal error: Class constant Person::AGE cannot have type never in
PHP Fatal error: Class constant Person::GENDER cannot have type callable in
严格类型行为
无论在 PHP 脚本中声明的 strict_types
行为如何,类常量总是被严格求值。
<?php
declare(strict_types=0);
class Test {
const string CONST = 1;
}
报错如下:
PHP Fatal error: Cannot use int as value for class constant Test::CONST of type string in
类型缩小
与返回类型类似,类常量类型也可以 "缩小",或在子类或实现中保持不变。
<?php
class Father {
public const string|int NAME = '王大毛';
}
class Child extends Father {
//public const string NAME = '王小毛';
public const int NAME = 19;
}
echo CHILD::NAME;
如果子类对类型进行了扩大,那么 PHP 在编译时会发出一个致命错误,案例如下
<?php
class Father {
public const string|int NAME = '王大毛';
}
class Child extends Father {
// 子类对类型进行了扩大,将会报错
public const string|int|float NAME = '王小毛';
}
echo CHILD::NAME;
报错如下
PHP Fatal error: Type of Child::NAME must be compatible with Father::NAME of type string|int in
省略常量类型声明
如果父类声明了一个常量类型,则所有子类也必须声明一个兼容的类型。
<?php
class Father {
public const string|int NAME = "王大毛";
}
class Child extends Father {
// 没有类常量声明类型
public const NAME = "王小毛";
}
报错如下
PHP Fatal error: Type of Child::NAME must be compatible with Father::NAME of type string|int in
在 Child 类中,尝试重写 Father 类的常量 NAME 时,没有指定类型。根据 PHP 的类型提示规则,子类中的常量应该与父类中的常量具有相同的类型或兼容的类型。不然就会报错。
反射 API 变化
可以使用反射API获取类常量的类型。
在 PHP 8.3 中,ReflectionClassConstant
类支持两个额外的方法:
class ReflectionClassConstant implements Reflector {
//...
public function getType(): ?ReflectionType {}
public function hasType(): bool {}
}
-
hasType()
方法返回常量是否使用常量声明; -
getType()
方法如果类常量没有声明类型,则返回 null,如果声明了类型,则返回一个ReflectionNamedType
对象。
案例如下
<?php
class Person {
public const string NAME = "王美丽";
}
$reflector = new ReflectionClassConstant('Person', 'NAME');
//print($reflector);
$r1 = $reflector->hasType();
$r2 = $reflector->getType();
var_dump($r1);
var_dump($r2);
结果如下
bool(true)
object(ReflectionNamedType)#2 (0) {
}