本文目录导读:

在 PHP 8.2 中,引入了 只读类(Readonly Classes) 的特性,这意味着你可以将一个类标记为 readonly,从而该类中的所有属性都会被隐式地设置为 readonly,简化了只读数据对象的创建。
基本语法
<?php
readonly class User {
public string $name;
public int $age;
public function __construct(string $name, int $age) {
$this->name = $name;
$this->age = $age;
}
}
// 使用
$user = new User('Alice', 30);
echo $user->name; // 输出: Alice
// 尝试修改属性会报错
// $user->name = 'Bob'; // Fatal error: Cannot modify readonly property
与普通 readonly 属性的区别
在 PHP 8.1 中,你可以单独声明只读属性:
class Product {
public readonly string $sku;
public string $name;
}
而 PHP 8.2 的只读类是更高级的语法糖,等价于将类中的所有属性都声明为 readonly。
关键特性
所有属性自动成为只读
readonly class Point {
// 不需要每个属性都写 readonly
public float $x;
public float $y;
public float $z;
// 构造函数中初始化
public function __construct(float $x, float $y, float $z = 0.0) {
$this->x = $x;
$this->y = $y;
$this->z = $z;
}
}
不允许声明静态属性
只读类不能包含静态属性:
readonly class Config {
// Fatal error: Readonly class Config cannot contain static properties
// public static string $appName = 'MyApp';
}
不允许声明动态属性
readonly class Demo {
public string $name;
public function __construct(string $name) {
$this->name = $name;
}
}
$demo = new Demo('Test');
// $demo->extra = 'value'; // Error: Cannot create dynamic property
类型声明是强制的
每个属性都必须有类型声明:
readonly class Item {
// Fatal error: Readonly property Item::$name must have type
// public $name;
public string $name; // 正确
}
支持继承
readonly class Base {
public string $baseProp;
}
// 不强制子类也是 readonly
class Child extends Base {
public string $childProp;
}
// 子类也可以是 readonly
readonly class ReadonlyChild extends Base {
public string $childProp;
}
可以定义方法和接口
interface Jsonable {
public function toJson(): string;
}
readonly class User implements Jsonable {
public function __construct(
public string $name,
public int $age
) {}
public function toJson(): string {
return json_encode([
'name' => $this->name,
'age' => $this->age
]);
}
}
实际应用示例
数据传输对象(DTO)
readonly class CreateUserRequest {
public function __construct(
public string $username,
public string $email,
public ?string $phone = null
) {}
}
// 使用
$request = new CreateUserRequest('john_doe', 'john@example.com');
echo $request->email; // john@example.com
配置对象
readonly class DatabaseConfig {
public function __construct(
public string $host,
public int $port,
public string $database,
public string $username,
public string $password
) {}
}
// 使用构造函数参数提升
$config = new DatabaseConfig(
host: 'localhost',
port: 3306,
database: 'myapp',
username: 'root',
password: 'secret'
);
值对象
readonly class Money {
public function __construct(
public float $amount,
public string $currency
) {
if ($amount < 0) {
throw new \InvalidArgumentException('Amount cannot be negative');
}
}
}
$price = new Money(99.99, 'USD');
不兼容的 PHP 版本
只读类仅在 PHP 8.2 及以上版本 可用,在较低版本中会报语法错误。
最佳实践建议
- 使用构造函数属性提升 - 结合简写构造函数声明更简洁
- 用于不可变对象 - 如 DTO、值对象、配置对象
- 不要过度使用 - 只读类不适合需要频繁修改状态的对象
- 注意序列化 - 需要特殊处理,因为
__sleep和__wakeup可能不适用
只读类对于建立不可变的数据对象非常有用,可以减少 bug 并使代码更易于理解。