php 8.3 无“只读类”语法,但可通过只读属性(readonly)、私有/受保护可见性、自定义序列化(__serialize/__unserialize)及反射验证(isreadonly)四方式实现逻辑只读性。

PHP 8.3 并没有“只读类”这个语法特性,但引入了只读属性(readonly properties),这是保护数据不被修改的核心机制。所谓“只读类”,实际是通过将所有关键属性声明为 readonly,并配合构造函数约束、访问控制和序列化防护,来实现类实例层面的逻辑只读性——即对象一旦创建,其核心状态不可变更。
以下是你真正需要关注的四点保护方式:
1. 用 `readonly` 关键字锁定属性赋值时机
只读属性只能在声明时或构造函数中赋值一次,之后任何写操作都会抛出 `Error: Cannot modify readonly property`。 例如:class User {
public function __construct(
public readonly string $id,
public readonly string $email,
private readonly array $roles = ['user']
) {}
}
- ✅ 允许:
new User('u1', 'a@b.com') - ❌ 禁止:
$user->email = 'x@y.com'或$user->roles[] = 'admin'(后者因数组本身可变,但$roles引用不可重赋)
注意:`readonly` 不冻结值内容(如数组元素、对象属性),只禁止对属性本身的重新赋值。
2. 配合私有/受保护可见性 + 只读,杜绝外部篡改
把只读属性设为 `private readonly` 或 `protected readonly`,再配以公共 getter 方法,能彻底隐藏写入口:class Config {
private readonly string $host;
private readonly int $port;
public function __construct(string $host, int $port) {
$this->host = $host;
$this->port = $port;
}
public function getHost(): string { return $this->host; }
public function getPort(): int { return $this->port; }
}
- 外部无法访问
$config->host(因private) - 即使改成
public readonly,也无法赋值(因readonly) - 二者叠加,才是双重保险
3. 序列化时主动接管,防止 `unserialize()` 绕过构造函数
PHP 默认反序列化会跳过构造函数,直接填充属性——这会破坏只读语义。解决办法是显式实现魔术方法:class User {
public function __construct(
public readonly string $id,
public readonly string $name
) {}
public function __serialize(): array {
return ['id' => $this->id, 'name' => $this->name];
}
public function __unserialize(array $data): void {
// 重新走构造逻辑,确保只读性不被破坏
$this->__construct($data['id'], $data['name']);
}
}
- ✅
serialize($user)正常工作 - ✅
unserialize($str)不会直接写$this->id,而是重建实例 - ❌ 不实现
__unserialize(),反序列化可能失败或触发未定义行为
4. 运行时用反射验证只读状态,增强调试与工具链支持
PHP 8.3 的 `ReflectionProperty::isReadOnly()` 可用于校验、ORM 映射或 API 序列化器中:$prop = new ReflectionProperty(User::class, 'id');
if ($prop->isReadOnly()) {
echo "该属性受只读保护,不可修改";
}
- 在自动映射 DTO 到数据库时,可跳过只读字段的更新逻辑
- 在日志或监控中识别高保障字段,辅助审计
不复杂但容易忽略
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











