php 8.1 不允许在接口中声明 readonly 属性,会触发“fatal error: interface cannot contain readonly properties”解析期致命错误;readonly 仅支持类实例属性和只读类,不支持接口、trait、抽象类(虽可声明但子类需自行初始化)。

不支持。PHP 8.1 不允许在接口(interface)中声明 readonly 属性。
接口只定义契约,不承载状态;而 readonly 是运行时实例级别的约束机制,依赖对象构造与内存分配,与接口的抽象语义冲突。PHP 解析器会在遇到此类语法时直接报错。
接口里写 readonly 会触发什么错误?
尝试如下代码:
<?php interface DataCarrier {
public readonly string $id;
}
PHP 8.1+ 会抛出致命错误:
Fatal error: Interface "DataCarrier" cannot contain readonly properties
该错误在解析阶段即发生,不会进入运行时。
那哪些地方能用 readonly 属性?
- ✅ 类(
class)中声明的实例属性(非 static、必须带类型) - ✅ 只读类(
readonly class)中所有公有属性自动为readonly,但仍需显式声明类型 - ❌ 接口(
interface) - ❌ Trait(
trait)——虽不报错,但 trait 中的readonly属性在被 use 时无法被正确初始化,行为未定义,官方明确不支持 - ❌ 抽象类(
abstract class)中可声明,但子类仍需在自身构造函数中完成初始化(不能延迟到子类)
替代方案:如何在接口约束下实现不可变语义?
接口本身不能强制“不可变”,但可通过组合方式逼近目标:
- 接口只声明 getter 方法(如
getId(): string),把“只读”语义推给实现类 - 实现类使用
public readonly string $id+ 构造参数自动赋值,满足接口且真正不可变 - 配合 PHPStan / Psalm 等静态分析工具,添加注解
@psalm-immutable或@immutable辅助检查
真正容易被忽略的一点是:很多人以为「接口定义了 readonly,实现类就能省略」,其实接口连声明都不允许——这并非遗漏,而是语言设计上对抽象与状态的严格分层。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











