PHP 8.2只读类使用PHPUnit模拟时报错的问题求助
解决PHPUnit 9.x模拟PHP 8.2 readonly类的编译错误
核心原因
PHP 8.2的readonly类是隐式final的,同时PHP语法规定:非readonly类不能继承readonly类。而PHPUnit 9.x的MockObject组件生成模拟类的方式是继承原类,这就触发了两个层面的错误:
- 试图继承一个隐式final的类
- 非readonly的Mock类试图继承readonly类
解决方案
1. 升级PHPUnit到10.1+版本
PHPUnit 10.1及以上版本的MockObject组件已经完全适配PHP 8.2的readonly类特性:
- 生成的模拟类会自动标记为
readonly且保持final特性 - 可以正常使用
createMock()或getMockBuilder()创建模拟对象
升级操作:
修改composer.json中的PHPUnit版本约束:
"phpunit/phpunit": "^10.1"
然后执行更新命令:
composer update phpunit/phpunit --with-all-dependencies
2. 无法升级时的替代方案
由于readonly类是隐式final的,无法通过继承生成Mock,可采用以下两种方式:
提取接口模拟
给readonly类定义对应的接口,测试时模拟接口而非具体类:
// 定义业务接口 interface PasswordProviderInterface { public function getRules(): array; } // 原readonly类实现接口 readonly class Provider implements PasswordProviderInterface { public function getRules(): array { // 业务逻辑实现 } } // 测试代码中模拟接口 $mock = $this->createMock(PasswordProviderInterface::class); $mock->method('getRules')->willReturn(['mock-password-rule']);
手动创建模拟实例
如果类逻辑简单,直接创建一个实现对应接口的readonly匿名类:
$mockProvider = new readonly class implements PasswordProviderInterface { public function getRules(): array { return ['mock-password-rule']; } };
注意事项
- 不要尝试修改readonly类的final特性,这违反了PHP 8.2 readonly类的设计规范
- PHPUnit 9.x已停止官方维护,升级到最新稳定版本是长期解决方案
内容的提问来源于stack exchange,提问作者Vitali Labatsevich
相关产品推荐
相关产品推荐

