You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

PHP 8.2只读类使用PHPUnit模拟时报错的问题求助

解决PHPUnit 9.x模拟PHP 8.2 readonly类的编译错误

核心原因

PHP 8.2的readonly类是隐式final的,同时PHP语法规定:非readonly类不能继承readonly类。而PHPUnit 9.x的MockObject组件生成模拟类的方式是继承原类,这就触发了两个层面的错误:

  1. 试图继承一个隐式final的类
  2. 非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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.04 04:35:32