PHP 8.1 中 readonly array 属性声明时不可赋默认值(如 = []),必须在 __construct() 中首次且唯一初始化为空数组;构造完成后任何整体赋值(含 = [])均触发 Fatal error,但数组内部增删改查仍合法。

PHP 8.1 中给 readonly array 属性赋空数组本身不会报错,但**在构造函数外再次赋值(包括赋空数组)会立即触发 Fatal error**。所谓“定义空数组报错”,绝大多数是误把二次赋值当成初始化,或混淆了声明语法与运行时行为。
只读属性声明阶段禁止任何默认值
以下写法直接导致语法错误,根本无法运行:
-
public readonly array $config = [];→ ❌ 编译失败:“readonly property cannot have default value” -
public readonly ?array $data = null;→ ❌ PHP 8.1 不支持 nullable 修饰 readonly 属性(8.2+ 才允许) -
private readonly array $items = ['a'];→ ❌ 即使是 private,带默认值仍非法
正确声明只有一条规则:仅类型 + readonly,不带等号和值。
初始化必须且只能发生在 __construct() 内
所有赋值操作必须集中到构造函数中完成,哪怕只是空数组:
立即学习“PHP免费学习笔记(深入)”;
-
合法:
$this->config = [];(最简初始化) -
合法:
$this->config = $input ?? [];(带兜底的参数接收) -
合法:
$this->config = $this->buildDefaults();(方法返回空数组也行)
一旦构造完成,任何对 $obj->config 的整体赋值——包括 $obj->config = [];、$obj->config = null;、unset($obj->config);——都会抛出:
Fatal error: Uncaught Error: Cannot modify readonly property
常见误判场景:你以为是“定义报错”,其实是“运行时报错”
这些情况常被归因为“空数组问题”,实际根源是违反 readonly 生命周期规则:
- 在
__construct()之外的任意方法(如init()、reset()、__set())里写了$this->data = []; - 使用属性提升时漏掉参数传递:
function __construct(public readonly array $config) {},但实例化时没传值 → 报错不是“空数组”,而是“readonly property must be initialized” - 继承子类在自己的
__construct()中试图重新赋值父类的 readonly 属性(即使赋的是空数组)→ 不允许,会 fatal - 用反射或魔术方法绕过访问控制尝试修改 → 一律拦截,无例外
空数组本身完全安全,问题出在“谁赋的、什么时候赋的”
只要满足两个条件,[] 就是最常用、最稳妥的初始化方式:
- 声明时不带任何初始值
- 第一次且唯一一次赋值发生在
__construct()函数体内
后续可自由操作数组内容:$obj->config['host'] = 'localhost';、unset($obj->config['port']); 都合法——readonly 锁定的是属性引用,不是数组元素。



















