在ThinkPHP 6+中,通过监听AppInit/AppEnd事件实现轻量级性能分析最稳妥:配置驱动开关、内存暂存数据、microtime(true)计时、memory_get_usage(true)测内存,避免hook dispatch或使用trace日志。

怎么在 ThinkPHP 里加一个轻量级性能分析钩子
直接改 App 类行为是最稳的路,ThinkPHP 6+ 的 App 实例在生命周期中可被监听和增强,不需要动核心文件。重点不是“加功能”,而是“不破坏原有流程”——所有分析逻辑必须可开关、不抛异常、不影响正常响应。
- 在
app/bootstrap.php或服务提供者里,用think\Container::getInstance()->bind('think\App', ...)替换 App 类时做装饰,或更推荐:监听AppInit和AppEnd事件 - 避免在
__construct或run里硬编码埋点,否则升级 ThinkPHP 时极易冲突 - 时间采集用
microtime(true),别用time();内存用memory_get_usage(true),否则小对象抖动会干扰判断
为什么不能直接 hook dispatch() 而要监听 AppInit/AppEnd
因为 dispatch() 可能被中间件提前终止(比如权限拦截返回 403),或者被路由重定向绕过,导致耗时统计不全。而 AppInit 是框架刚初始化完容器、配置后的第一站,AppEnd 是响应已写入、资源待释放的最后一站——这两点才真正对应一次请求的“始”与“终”。
-
AppInit事件里记录起始时间、内存、当前路由信息($app->http->getName())、请求方法 -
AppEnd事件里计算差值,并把结果写进$_SERVER['performance_log']或临时文件,供后续导出 - 如果用了 Swoole,注意
AppEnd在协程里可能被复用,得用Co::getUid()隔离日志上下文
如何让分析数据在开发环境自动显示但不污染生产环境
靠配置驱动,而不是 if-else 包裹逻辑。ThinkPHP 的 Env 和 Config 是天然开关,别自己造 DEBUG_PERF 常量。
- 在
config/app.php加一项:'performance_trace' => env('APP_PERF_TRACE', false) - 只在
AppInit监听器里读这个配置,false就直接 return,连计时器都不启 - 想看 HTML 埋点?用
think\Response的withHeader()注入X-Perf-Time,别往 body 里塞 div —— 某些前端框架会因 DOM 变动触发重绘,反而扭曲真实耗时
常见错误:用 trace() 或 Log::record() 记性能数据
trace() 是调试辅助函数,底层走的是 Log 通道,而日志写入本身就有 IO 开销(尤其是文件驱动),会把 5ms 的控制器执行拉长到 20ms+,测出来全是噪音。这不是“分析性能”,是在“制造性能问题”。
立即学习“PHP免费学习笔记(深入)”;
- 性能数据必须存在内存数组里,
AppEnd时一次性 dump 到php://output或syslog()(后者需开log_facility) - 如果真要落盘,用
fopen(..., 'a')+fwrite()+fflush(),跳过 ThinkPHP 日志中间层 - 别在循环里调
debug_backtrace()——单次调用就可能吃掉 1~3ms,尤其在有大量 trait 的项目里
最易被忽略的一点:Swoole 环境下,$_SERVER 里的请求时间戳不可信,得用 $_SERVER['request_time_float'] 或 Swoole\Coroutine::getStats() 获取真实发起时间。否则你看到的“总耗时”可能是上一个请求的尾巴。



















