简介:
自 PHP 8.0 发布以来,该语言经历了一系列重要的功能迭代与性能改进。对于开发者而言,系统理解这些新特性不仅是技术更新的需要,更是优化代码设计、降低维护成本的有效途径。本分类将按版本梳理 PHP 8.x 的新特性。
8.2 官方手册参考指南(含其他特性):官方链接
一、只读类
PHP8.2 引入的只读类 (Readonly Classes) 是对 PHP8.1 只读属性的重大扩展,它允许开发者将整个类标记为只读,从而自动使所有实例属性变为只读并禁止动态属性。这一特性极大地简化了不可变对象的创建,增强了代码的安全性和可维护性。
核心特性:
- 自动只读属性:将类标记为
readonly后,所有声明的实例属性都会自动获得readonly修饰符,无需逐个添加。 - 禁止动态属性:只读类完全禁止创建动态属性,即使使用注解
#[AllowDynamicProperties]属性也会触发编译错误。 - 静态属性限制:只读类不能包含任何静态属性,无论是公共、受保护还是私有静态属性都会导致编译错误。
- 类隐式为 final:只读类默认是隐式 final 的,即不能被继承。这是为了确保不可变性不会被子类破坏------如果允许继承,子类可能添加可变属性或覆盖父类行为,从而违背只读类的设计初衷。
- 初始化时机:只读属性的赋值只能发生在以下两个时刻 声明时直接赋默认值、构造函数中赋值
- 克隆行为:克隆一个只读类对象时,只读属性在克隆过程中可以被修改(即在
__clone()方法中重新赋值)
示例:
php
<?php
declare(strict_types=1);
readonly class Example
{
public function __construct(
public string $name,
public int $age,
public string $email
){}
public function withInfo(string $msg): self
{
return new self(
$this->name . '-'. $msg,
$this->age,
$this->email
);
}
}
$exampleClass = new Example('小寒', 18, 'xxxx@qq.com');
$user = $exampleClass->withInfo('本地人');
var_dump($user->name); // 读操作输出:"小寒-本地人"
//$user->name = '小小'; // 写操作抛出 Error
推荐使用场景:
- 数据传输对象(DTO):数据传递,不被修改。防止中间层篡改,简化声明,类型安全
- 值对象(Value Object):不可变性,业务方法返回新实例。强制不可变,支持返回新实例的业务方法
- 配置对象:用于集中管理应用的配置信息(如数据库连接、API 密钥、功能开关),防止运行时配置被误改,类型安全
- 事件/消息对象:历史记录不可篡改。保证历史完整性,序列化友好,可预测
二、析取范式 (DNF)类型
PHP 8.2 引入了析取范式(DNF,Disjunctive Normal Form)类型,正式支持将联合类型与交集类型以规范化的方式组合使用。这一特性极大地提升了类型声明的表达能力,让开发者能够用更精确、更可读的方式描述复杂的类型约束。
核心规则:在 PHP 的类型系统中,DNF 类型允许开发者声明由交集类型(合取)组成的联合类型(析取),当同时使用联合类型和交集类型时,所有交集类型必须用括号明确分组。
语法示例:
php
<?php
declare(strict_types=1);
// DNF 类型的基本形式:(TypeA & TypeB) | TypeC | TypeD
class Example
{
public function bar((A & B) | null $entity) {
return $entity;
}
}
/********** ✅ 合法 DNF 类型示例 **************/
// 简单的联合类型(本身就是 DNF)
function fn1(): A | B | C {}
// 包含一个交集类型的联合类型
function fn2(): (A & B) | C | D {}
// 包含多个交集类型的联合类型
function fn3(): (A & B) | (C & D) | E {}
// 可空交集类型
function fn4(): (A & B) | null {}
/******** ❌ 非法 DNF 类型示例(会导致解析错误)********/
// 错误:交集类型未使用括号分组
function fnErr1(): A & B | C {} // 正确写法:(A & B) | C
// 错误:嵌套的交集和联合
function fnErr2(): A | (B & (C | D)) {} // 正确写法:A | (B & C) | (B & D)
// 错误:联合类型嵌套在交集中
function fnErr3(): A & (B | C) {} // 正确写法:(A & B) | (A & C)
最佳实践:
-
需要表达可空的交集类型时:
(TypeA & TypeB) | null是 DNF 类型最经典的用法 -
函数返回多种类型,其中某些是交集类型时
-
需要明确表达复杂的业务类型约束时
三、独立类型(null、false、true)
PHP 8.2 对类型系统进行了重要扩展,正式允许null、false 和 true 作为独立类型使用,不再局限于联合类型的一部分PHP。这一特性被 PHP 官方描述为 "编程增强",使类型系统更具表现力和完整性,能够精确声明返回类型、参数类型和属性类型,特别是在表达 "永远返回 false" 或 "永远返回 null" 等场景时。
null 类型:单元类型(unit type),只包含一个值null。
false/true 类型:单例类型(singleton type),分别只包含false和true两个布尔值
bool 类型:本质上等价于true|false联合类型
语法示例:
-
独立类型声明位置:可用于函数 / 方法参数、返回值和类属性。
php<?php declare(strict_types=1); // 参数类型 function acceptParams(null $value1, false $value2, true $value3): void{} // 返回类型 function returnParams(): null{ return null; } // 类属性 class Example { public null $emptyValue = null; public false $isDisabled = false; public true $isEnabled = true; } -
联合类型中的使用:需遵循特定规则->不允许在联合类型中出现
true|false这样的组合,因为这与bool完全重复,会造成类型系统的歧义和冗余。php<?php declare(strict_types=1); function findUser(int|string|false $id): Array|false { return false; } function findValue(string|null $value): true|null { return null; } /** ❌ 非法用法(会抛出编译时错误) */ function invalid1(true|bool $value): void {} // 冗余的类型,触发编译时致命错误 function invalid2(true|false $value): void {} // 同样是冗余声明,true|false 的并集恰好就是 bool 类型本身
最佳实践:
- 优先使用独立类型表达精确意图:当函数 / 方法永远返回固定布尔值或 null 时,使用独立类型替代 bool 或可空类型。
- 避免过度使用:仅在确实需要精确类型表达时使用,不要为了 "新特性" 而滥用。
- 区分 null 和 false 的语义:用 null 表示 "无值 / 特殊状态",用 false 表示 "操作失败 / 错误状态"。
- 不要用 null 类型替代 void:无返回值的函数应使用 void 类型。
限制与注意事项:
- 类型冗余检测:检测冗余类型声明并抛出致命错误。
- 非强制类型转换:即使禁用严格类型模式,独立类型也不会强制转换其他值。
- null 类型特殊限制:不允许
?null语法(冗余)、声明null返回类型的函数必须显式返回 null,否则抛出 TypeError;而void类型表示无返回值。
四、新的随机(Random)扩展
PHP 8.2 正式引入全新的Random 扩展,提供了一套面向对象的随机数生成 API,彻底重构了 PHP 的随机数生成体系。该扩展解决了传统随机数函数(如rand()、mt_rand())的诸多缺陷,同时保持向后兼容性,为开发者提供更安全、更灵活、更可预测的随机数生成方案。
示例:
php
<?php
declare(strict_types=1);
use Random\Randomizer;
// 创建默认Randomizer实例(可内置引擎 例如使用Secure加密安全引擎)
$randomizer = new Randomizer();
//$randomizer = new Randomizer(new Secure());
// 生成1-100之间的随机整数
$randomNumber = $randomizer->getInt(1, 100);
// 生成32字节随机数据(适合令牌、盐值)
$randombytes = $randomizer->getBytes(32);
$token = bin2hex($randombytes);
// 从指定字符集中生成随机字符串
$randomString = $randomizer->getBytesFromString('abcdefghijklmnopqrstuvwxyz0123456789',16). ".example.com";
// 打乱数组并返回新数组
$array = ['a', 'b', 'c', 'd'];
$shuffledArray = $randomizer->shuffleArray($array);
// 打乱字符串并返回新字符串
$shuffledString = $randomizer->shuffleBytes('hello world');
// 随机挑选数组键
$colors = ['red' => '', 'green' => '', 'blue' => '', 'yellow' => ''];
$selectedKeys = $randomizer->pickArrayKeys($colors, 2);
echo '<pre>';
var_dump($randomNumber); // 23
var_dump($token); // d1ef5662f481d99f641caa8590f54ba37c5322d6a8358e8784fcec539ddb2555
var_dump($randomString); // wybe6bhjcbe0km0j.example.com
var_dump($shuffledArray); // ['d','b','c','a']
var_dump($shuffledString); // wolhelrd lo
var_dump($selectedKeys); // ['red','green']
最佳实践:
- 默认安全:创建 Randomizer 实例时不指定引擎,自动使用 Secure 引擎(密码、令牌、密钥等安全敏感数据必须使用)。
- 明确引擎选择:在代码中显式声明引擎类型,提高可读性和可维护性。
- 避免全局状态:为每个逻辑单元创建独立的 Randomizer 实例,特别是在并发环境中。
- 测试与复现:在测试环境使用固定种子的伪随机引擎,确保测试可复现。
- 资源释放:Randomizer 实例无需手动释放,PHP 会自动管理内存。