PHP 8.1 中 readonly 数组属性不可设默认值,必须在构造函数中初始化(如 $this->config = []);readonly 仅禁止整体赋值,不限制数组内部增删改查。

PHP 8.1 中无法直接用 public readonly array $data = []; 定义空数组——因为 readonly 属性**不允许带默认值**,必须在构造函数中显式初始化。所谓“延迟初始化”,其实是指把数组的创建和赋值推迟到 __construct() 内完成,而非声明时硬编码,这样既满足语法约束,又保留灵活性。
只读数组属性的正确声明方式
声明时只需类型+readonly,不带任何初始值:
-
public readonly array $config;✅ 合法声明 -
public readonly array $config = [];❌ 致命错误:readonly 属性不能有默认值 -
public readonly ?array $config;❌ 不合法:PHP 8.1 不支持 nullable 类型用于 readonly(8.2+ 才允许readonly ?string等)
在构造函数中完成初始化(含空数组场景)
所有初始化逻辑必须落在 __construct() 里,包括创建空数组、从参数填充、或按条件决定内容:
- 最简情况:直接赋空数组
$this->config = []; - 带参数:接收外部数据并赋值
$this->config = $input ?? []; - 动态构建:根据环境/配置生成结构
$this->config = $this->loadDefaults();
注意:一旦构造完成,$obj->config = [] 或 $obj->config = ['a' => 1] 都会触发 Fatal error: Cannot modify readonly property。
立即学习“PHP免费学习笔记(深入)”;
空数组 ≠ 内容不可变,这点必须清醒
声明 public readonly array $config; 并初始化为空数组后,你仍可安全执行:
-
$obj->config['host'] = 'localhost';✅ 允许写入键值 -
$obj->config[] = 'debug';✅ 允许追加元素 -
unset($obj->config['host']);✅ 允许删除键
readonly 只锁住“整个变量被替换”这一动作,不锁数组内部操作。若真需要内容级只读(比如配置不可被任何方式修改),得换方案:ArrayObject::FLAG_PROTECT 或封装 getter 返回副本。
兼容旧版或增强防护的实用建议
如果项目需兼顾 PHP
- 用 private 属性 + public getter,返回
array_merge([], $this->data)避免外部持有原数组引用 - 在 PHPDoc 中标注
@property-read array $config,配合 PHPStan 提前发现非法赋值 - 对关键配置对象,升级到 PHP 8.2+ 后改用
readonly class Config { ... },自动锁定全部属性
只读不是万能锁,而是精准锚点——它守住的是“谁在什么时候能改这个变量”,而不是“这个变量里装的东西能不能动”。用对地方,才能真正稳住数据边界。



















