本文目录导读:

在 PHP 8.2 中引入了只读类(Readonly Classes),这是一个重要的特性,但它也伴随着一系列严格的限制,以下是完整的限制清单:
属性限制
所有属性必须显式声明类型
readonly class User {
public $name; // ❌ 致命错误:必须声明类型
public string $email; // ✅ 正确
}
属性必须是 typed 且不能有默认值
readonly class User {
public string $name = 'John'; // ❌ 致命错误:不能有默认值
public string $email; // ✅ 正确
}
禁止使用 static 属性
readonly class User {
public static string $count = '0'; // ❌ 致命错误:不能有静态属性
}
继承限制
只读类不能被子类继承为普通类
readonly class ParentClass {}
class ChildClass extends ParentClass {} // ❌ 致命错误
非只读类不能继承只读类
class ParentClass {}
readonly class ChildClass extends ParentClass {} // ❌ 致命错误
但只读类可以继承只读类
readonly class ParentClass {}
readonly class ChildClass extends ParentClass {} // ✅ 正确
特质(Trait)限制
只读类不能使用含有非只读属性的 trait
trait HasName {
public string $name = 'John'; // 有默认值
}
readonly class User {
use HasName; // ❌ 致命错误
}
只读类可以使用只读属性的 trait
trait HasEmail {
public string $email; // ✅ 符合只读类要求
}
readonly class User {
use HasEmail; // ✅ 正确
}
属性修饰符限制
不允许使用 var 关键字
readonly class User {
var string $name; // ❌ 致命错误
public string $email; // ✅ 正确
}
不允许使用 static 修饰符
readonly class User {
public static string $count; // ❌ 致命错误
public string $email; // ✅ 正确
}
动态属性限制
禁止动态创建属性
readonly class User {
public string $name;
}
$user = new User();
$user->age = 25; // ❌ 致命错误:不能创建动态属性
序列化限制
不兼容 __wakeup() 和 __unserialize()
readonly class User {
public string $name;
public function __wakeup() {} // ❌ 致命错误
// 或
public function __unserialize(array $data) {} // ❌ 致命错误
}
但 __serialize() 仍然可以
readonly class User {
public string $name;
public function __serialize() { // ✅ 可以(但有警告)
return ['name' => $this->name];
}
}
克隆限制
__clone() 方法不能修改属性
readonly class User {
public string $name;
public function __clone() {
$this->name = 'New Name'; // ❌ 致命错误:不能修改只读属性
}
}
反射限制
ReflectionClass::newInstanceWithoutConstructor() 会创建未初始化状态
$reflection = new ReflectionClass(User::class); $user = $reflection->newInstanceWithoutConstructor(); echo $user->name; // ❌ 错误:未初始化的属性
属性初始化限制
只能在构造函数中初始化(仅一次)
readonly class User {
public string $name;
public function __construct(string $name) {
$this->name = $name; // ✅ 首次初始化
$this->name = 'New'; // ❌ 致命错误:不能再次赋值
}
}
访问级别限制
不允许使用 protected 或 private 属性
readonly class User {
protected string $name; // ❌ 致命错误
private string $email; // ❌ 致命错误
public string $phone; // ✅ 正确,只能是 public
}
只读类的核心设计目的是创建不可变的数据对象,这些限制确保了:
- 类型安全 - 所有属性必须显式声明类型
- 不可变性 - 属性只能在构造函数中初始化一次
- 简化继承 - 防止复杂的继承场景
- 序列化安全 - 防止反序列化时修改只读属性
使用只读类时,需要仔细考虑这些限制,确保设计符合实际需求。