PHP 8.2 新特性
PHP 8.2 引入了只读类(Readonly Classes)、DNF 类型(Disjunctive Normal Form Types)、null/false/true 作为独立类型、敏感参数隐藏(SensitiveParameter)以及 trait 中的常量等特性,进一步增强了 PHP 的类型安全性和表达能力。
前置知识
阅读本节前,建议先了解:PHP 8.1 新特性
只读类(Readonly Classes)
基本用法
php
<?php
declare(strict_types=1);
// PHP 8.1 需要每个属性都标记 readonly
class UserOld
{
public readonly string $name;
public readonly string $email;
public readonly int $age;
}
// PHP 8.2+ 只读类:所有属性自动为 readonly
readonly class User
{
public function __construct(
public string $name,
public string $email,
public int $age,
) {}
// 所有属性都是 readonly,不能被修改
}
// 只读类与只读属性可以混用
readonly class Configuration
{
public string $appVersion; // 自动 readonly
public function __construct(
string $version,
) {
$this->appVersion = $version;
}
}只读类限制
- 只读类的所有属性都必须是 typed(有类型声明)
- 只读类不能有动态属性
- 只读类的子类也必须是 readonly
DNF 类型(Disjunctive Normal Form Types)
基本概念
php
<?php
declare(strict_types=1);
// DNF 类型允许联合类型中包含交叉类型
// 语法:A|(B&C) 表示 A 或 (B 和 C)
function processInput((string&HasId)|(int&HasName) $input): string
{
// $input 要么是 string&HasId,要么是 int&HasName
if ($input instanceof HasId && is_string($input)) {
return "String with ID: {$input->getId()}";
}
return "Int with Name: {$input->getName()}";
}
// 实际应用场景
function handleEntity(
(Countable&Traversable) | array $collection,
): int {
return count($collection);
}
// 更复杂的 DNF 类型
interface Renderable {}
interface Formattable {}
function render(
(Renderable & Formattable) | string | null $input,
): string {
return match (true) {
$input === null => '',
is_string($input) => $input,
default => $input->format(),
};
}独立类型 null、false、true
null 类型
php
<?php
declare(strict_types=1);
// PHP 8.2 之前:只能通过 nullable (?) 表示
function findUser(int $id): ?User { }
// PHP 8.2+:null 可以作为独立类型(在联合类型中)
function findUser2(int $id): User|null { }
// null 只能用于联合类型(不能单独使用)
function process(null $value): void {} // ❌ 错误
function process(null|int $value): void {} // ✅ 正确false 类型
php
<?php
declare(strict_types=1);
// PHP 8.2+ false 可以作为独立类型
// 主要用于函数可能返回 false 的场景
// strpos() 的返回类型声明
function strpos(string $haystack, string $needle, int $offset = 0): int|false
{
$pos = \strpos($haystack, $needle, $offset);
return $pos === false ? false : $pos;
}
// file_get_contents 可能返回 false
function readFileContent(string $path): string|false
{
$content = file_get_contents($path);
return $content === false ? false : $content;
}
// 数据库查询
function fetchRow(PDOStatement $stmt): array|false
{
$row = $stmt->fetch(PDO::FETCH_ASSOC);
return $row === false ? false : $row;
}
// 使用
$result = strpos('Hello World', 'World');
if ($result === false) {
echo "Not found\n";
} else {
echo "Found at position {$result}\n";
}true 类型
php
<?php
declare(strict_types=1);
// true 作为独立类型(PHP 8.2+)
function alwaysReturnsTrue(): true
{
return true; // 只能返回 true
}
function validateConfig(array $config): true
{
// 验证配置
if (!isset($config['key'])) {
throw new InvalidArgumentException('Missing key');
}
return true; // 验证通过返回 true
}
// true 在联合类型中的使用
function checkFeature(string $feature): bool
{
return match ($feature) {
'new_ui' => true,
'legacy_mode' => false,
default => throw new InvalidArgumentException("Unknown feature"),
};
}敏感参数隐藏(SensitiveParameter)
php
<?php
declare(strict_types=1);
use SensitiveParameter;
// 在堆栈跟踪中隐藏敏感参数值
class AuthService
{
public function login(
string $email,
#[SensitiveParameter]
string $password,
): bool {
// ...
return true;
}
public function connect(
string $host,
int $port,
#[SensitiveParameter]
string $apiKey,
): void {
// ...
}
}
// 当异常发生在 login() 方法中时,
// 堆栈跟踪中的 password 参数值会被显示为 [object] (SensitiveParameter)
// 而非实际的密码值Trait 中的常量
php
<?php
declare(strict_types=1);
// PHP 8.2 之前:Trait 中不能定义常量
// PHP 8.2+:Trait 可以定义常量
trait HasTimestamps
{
public const string CREATED_AT = 'created_at';
public const string UPDATED_AT = 'updated_at';
public function getCreatedAt(): ?string
{
return $this->{self::CREATED_AT};
}
public function setCreatedAt(DateTimeInterface $date): void
{
$this->{self::CREATED_AT} = $date->format('Y-m-d H:i:s');
}
}
trait HasSoftDeletes
{
public const string DELETED_AT = 'deleted_at';
public function trashed(): bool
{
return $this->{self::DELETED_AT} !== null;
}
}
class User
{
use HasTimestamps;
use HasSoftDeletes;
public function __construct(
private array $attributes = [],
) {}
public function __get(string $key): mixed
{
return $this->attributes[$key] ?? null;
}
}
// 访问 trait 常量
echo User::CREATED_AT; // "created_at"
echo User::UPDATED_AT; // "updated_at"
echo User::DELETED_AT; // "deleted_at"其他改进
readonly 属性可以被重新初始化(通过 cloning)
php
<?php
declare(strict_types=1);
readonly class Point
{
public function __construct(
public float $x,
public float $y,
) {}
// __clone 中可以修改 readonly 属性
public function __clone()
{
$this->x = $this->x * 2;
$this->y = $this->y * 2;
}
}
$original = new Point(1.0, 2.0);
$clone = clone $original; // $clone->x = 2.0, $clone->y = 4.0新的随机数扩展
php
<?php
declare(strict_types=1);
use Random\Randomizer;
use Random\Engine\Xoshiro256StarStar;
// PHP 8.2+ 新的随机数生成器
// 使用 Randomizer
$randomizer = new Randomizer();
// 随机整数
$int = $randomizer->getInt(1, 100);
// 从数组中随机选择
$item = $randomizer->pick(['a', 'b', 'c', 'd']);
// 打乱数组
$shuffled = $randomizer->shuffleArray([1, 2, 3, 4, 5]);
// 随机字节
$bytes = $randomizer->getBytes(16);
// 密码安全字符串
$password = $randomizer->getBytesFromString(
'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!@#$%',
16,
);
// 自定义引擎
$engine = new Xoshiro256StarStar(seed: 42);
$randomizer = new Randomizer($engine);最佳实践
- 使用 readonly class:对于值对象(VO)和不可变实体
- 使用 DNF 类型:精确表达复杂的类型约束
- 使用 false 类型:准确声明可能失败的函数返回类型
- 使用 SensitiveParameter:保护密码、API Key 等敏感信息
- 使用 trait 常量:在 trait 中定义共享常量
下一节
继续学习:PHP 8.3 新特性