PHP 8.3 新特性 - Typed Class Constants

作者: 温新

图书: 【PHP 8.3 新特性】

阅读: 569

时间: 2024-11-23 13:11:07

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 类型。

以下类型不支持作为类常量类型:

  • voidnever 类型:这些类型仅用于返回类型;
  • 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) {
}
请登录后再评论