PHP7.1/7.2下PHPUnit测试比PHP7.0慢3倍问题排查求助
我之前也碰到过类似的情况,而且确实有不少开发者反馈过PHP7.1及以上版本在运行Laravel这类框架的批量PHPUnit测试时,会出现比PHP7.0慢很多的情况。结合你的测试表现(单个测试速度正常,批量就变慢,移除慢测试后另一个会“顶替”),本质是累积的初始化开销触发了性能瓶颈,而非单个测试本身有问题。下面给你梳理已知原因、排查工具和针对性解决方法:
已知的PHP7.1/7.2 PHPUnit慢速问题
PHP7.1开始在OPcache默认配置、垃圾回收机制上做了调整,加上Laravel测试套件的初始化(比如数据库迁移、框架实例创建)开销在批量运行时被放大,就会出现这种整体速度骤降的情况。尤其是你用了RefreshDatabase trait,每次测试都要重建数据库,这个操作的开销在PHP7.1+下可能被进一步放大。
排查工具推荐
除了你已经用的johnkary/phpunit-speedtrap,这些工具能帮你精准定位根源:
1. Xdebug Profiling(针对性启用,不用全局开)
不要全局开启Xdebug,而是用它的 profiling 功能只针对测试运行:
- 在Homestead里找到对应PHP版本的
php.ini(比如PHP7.2的是/etc/php/7.2/cli/php.ini),添加/修改:xdebug.profiler_enable = 0 xdebug.profiler_enable_trigger = 1 xdebug.profiler_output_dir = /home/vagrant/profiler - 手动创建输出目录:
mkdir /home/vagrant/profiler - 运行测试时触发 profiling:
XDEBUG_PROFILE=1 phpunit - 用
qcachegrind(Linux/macOS)或者wincachegrind(Windows)打开生成的.grind文件,重点看函数调用的累积耗时——比如Laravel的数据库迁移、框架初始化相关的函数,或者PHP核心的垃圾回收、序列化函数的调用次数和耗时。
2. OPcache状态检查
PHP7.1+的CLI模式默认可能没开启OPcache,或者配置不合理导致频繁重新编译脚本,累积开销极大。运行以下命令查看OPcache状态:
php -r 'var_dump(opcache_get_status());'
重点关注这几个参数:
opcache.enable_cli:如果是false,赶紧改成true(CLI模式下OPcache默认可能关闭)opcache.memory_consumption:如果内存不足,会导致脚本频繁被逐出缓存opcache.max_accelerated_files:如果测试涉及的文件数超过这个值,也会触发重新编译
3. 手动跟踪测试初始化耗时
在你的测试基类(TestCase.php)里加一段代码,打印每次setUp的耗时,看是否批量运行时耗时逐渐增加:
protected function setUp(): void { $start = microtime(true); parent::setUp(); $end = microtime(true); error_log("当前测试SetUp耗时:" . number_format($end - $start, 3) . "秒"); }
如果setUp耗时越来越长,说明垃圾回收没及时清理框架实例,或者数据库连接的累积开销在PHP7.1+下被放大。
针对性解决方法
1. 优化OPcache配置
修改对应PHP版本的opcache.ini(比如PHP7.2的是/etc/php/7.2/cli/conf.d/10-opcache.ini),确保以下配置:
opcache.enable=1 opcache.enable_cli=1 ; 关键!CLI模式必须开启OPcache opcache.memory_consumption=256 opcache.max_accelerated_files=20000 opcache.validate_timestamps=0 ; 测试环境关闭文件时间校验 opcache.revalidate_freq=0 opcache.save_comments=1 ; Laravel依赖注释,不能关
修改后重启PHP-FPM(CLI模式下次运行自动生效),再跑测试看看速度变化。
2. 调整Laravel测试的数据库策略
虽然你用了内存SQLite,但RefreshDatabase每次测试都重新迁移的开销在PHP7.1+下可能更明显:
- 尝试改用
DatabaseMigrationstrait,或者手动控制迁移时机——比如在测试套件开始时只迁移一次,测试结束后清理数据(用truncate代替重新迁移) - 检查
config/database.php里的SQLite配置,确保foreign_key_constraints开启,同时确认:memory:数据库的连接配置正确
3. 调整PHP运行参数
PHP7.1+的一些默认参数可能不适合批量测试,尝试在运行测试时加上:
php -d zend.detect_unicode=0 -d memory_limit=-1 phpunit
或者在phpunit.xml里配置:
<php> <ini name="zend.detect_unicode" value="0"/> <ini name="memory_limit" value="-1"/> </php>
4. 升级Homestead和依赖
你的Homestead版本7.0.1比较旧,对应PHP7.2的早期版本,可能存在一些底层兼容性问题。尝试升级到最新的按项目安装的Homestead,同时确保Laravel、PHPUnit的版本是兼容的(Laravel 5.5推荐PHPUnit 6.x,你当前版本是符合的,但可以尝试更新到6.x的最新版)。
内容的提问来源于stack exchange,提问作者Michael

