只读类中空数组必须在构造函数内初始化,后续任何赋值(包括[]、array()或null)均触发fatal error。 readonly class user { public function __construct(public string $name, public array $tags = []); }

PHP 8.2 中定义空数组([] 或 array())本身不会报错,你在只读类(readonly class)里给属性赋空数组却触发错误,问题不在“空数组”,而在于**只读属性只能在构造函数中初始化一次,后续任何赋值(包括设为空数组)都会被拒绝**。
只读类中空数组必须在构造函数内完成初始化
只读类的所有属性一旦声明,就禁止在 __construct() 之外的任何位置写入——哪怕写的是 []、array() 或 null,PHP 都会立即抛出 Fatal error: Cannot assign to readonly property。
- ❌ 错误写法:在方法中补全只读数组属性
readonly class User {
public array $tags;
public function __construct(string $name) {
$this->name = $name;
$this->tags = []; // ✅ 这里可以
}
public function addTag(string $tag): void {
$this->tags[] = $tag; // ❌ 报错!不能修改只读属性
}
}
- ✅ 正确做法:所有只读数组属性必须在构造函数参数中接收并一次性赋值,不可留空再补
readonly class User {
public function __construct(
public string $name,
public array $tags = [], // ✅ 默认值或传入空数组均可
) {}
}
别用 setter 或 hydrate 方法绕过只读限制
试图通过普通方法、DTO 转换或 ORM 映射逻辑对只读属性二次赋值,都会失败。PHP 的只读检查是运行时强制的,不依赖类型提示或注解。
- 不要写
$user->tags = [];—— 即使目标是清空,也不允许 - 不要在
fromArray()工厂方法外调用new User(...)后再赋值 - ORM(如 Doctrine、Eloquent)若尝试自动填充只读属性,需关闭自动映射或改用构造函数注入方式
需要动态更新数组?换设计,不是换语法
只读类天然不适合频繁修改的字段。若业务确实要求增删标签、追加日志等操作,说明该属性不该是只读的。
- 方案一:改用普通类 + 显式 getter,用方法封装变更逻辑(如
withTags(array $newTags)返回新实例) - 方案二:保持只读类,但把可变数组抽离为独立服务或状态容器,User 只持有一个不可变快照
- 方案三:使用
readonly属性存原始数据,另配一个非只读的“操作代理”对象处理变更
注意静态属性与只读类的组合陷阱
只读类不影响静态属性,但若你在静态方法中尝试给只读实例的属性赋空数组,同样会失败:
- ❌
self::$cache[$id]->tags = [];—— 不管在哪调用,都违法 - ✅ 正确缓存方式:缓存完整新实例,例如
self::$cache[$id] = new User($name, []);
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











