在 ThinkPHP5 里接入 XHProf,关键不是单独写一段分析代码,而是把它放进框架请求生命周期里,让每次访问都能自动记录性能数据。下面按“绑定钩子、创建开始行为、创建结束行为、查看结果”四步整理,便于你判断这套方案是否适合本地调试或日常性能排查。
先理解思路:为什么要借助钩子和行为
ThinkPHP 中的“行为”可以理解为应用执行过程中的一个动作,而“钩子”则是这些动作被触发的位置。把 XHProf 接到钩子上,最大的好处是不用在每个控制器或方法里重复写性能分析代码。
如果你对 xhprof 本身还不熟悉,可以先看前一篇介绍;如果你对 ThinkPHP 的 钩子 和 行为 机制了解不多,也建议先补一下官方手册。本文重点放在接入方式本身:让分析在应用启动时开启,在请求结束时统一落盘并生成查看入口。
在 tags.php 中绑定分析开始与结束行为
第一步是在 application/tags.php 中注册行为。这里的目标很明确:

- 应用初始化时开启性能分析;
- 应用结束时关闭性能分析并保存结果。
原始配置如下:
return [
// 应用初始化
'app_init' => [
'app\common\behavior\XhprofEnable',
],
// ...省略其他代码
// 应用结束
'app_end' => [
'app\common\behavior\XhprofDisable',
],
// ...省略其他代码
];
这里的意思是,把性能分析行为分别挂到 app_init 和 app_end 两个阶段。这样配置后,请求一进入应用就开始统计,执行完成后再统一收集数据。
需要注意的是,后文实际创建的类名是 XhprofStart 和 XhprofEnd,因此如果你直接照着代码落地,行为类名与 tags.php 中的绑定名称要保持一致。否则钩子虽然写了,运行时却找不到对应行为类。
创建开始分析行为:在请求入口启用 XHProf
接下来创建开始分析的行为文件:
application/common/behavior/XhprofStart.php
<?php
namespace appcommonbehavior;
/**
* 开始性能分析
*/
class XhprofStart
{
public function run($params)
{
xhprof_enable(XHPROF_FLAGS_NO_BUILTINS | XHPROF_FLAGS_CPU | XHPROF_FLAGS_MEMORY);
}
}
这段代码只做一件事:在请求开始时调用 xhprof_enable()。启用时带了三个标志位:
XHPROF_FLAGS_NO_BUILTINS:排除内建函数,减少无关噪音;XHPROF_FLAGS_CPU:记录 CPU 相关开销;XHPROF_FLAGS_MEMORY:记录内存使用情况。
对排查接口慢、函数调用链长、内存占用异常这类问题,这样的采样信息通常已经够用。它适合放在开发环境或本地调试环境里持续观察,不适合长期对线上全部请求无差别开启。
创建结束分析行为:保存结果并生成查看入口
然后是请求结束时执行的行为文件:

application/common/behavior/XhprofEnd.php
<?php
namespace appcommonbehavior;
/**
* 结束性能分析
*/
class XhprofEnd
{
public function run($params)
{
$xhprof_data = xhprof_disable();
// display raw xhprof data for the profiler run
// print_r($xhprof_data);
// 这里是xhprof的目录 !!!
$XHPROF_ROOT = realpath(dirname(env('APP_PATH')) . '/../xhprof');
include_once $XHPROF_ROOT . "/xhprof_lib/utils/xhprof_lib.php";
include_once $XHPROF_ROOT . "/xhprof_lib/utils/xhprof_runs.php";
// save raw data for this profiler run using default
// implementation of iXHProfRuns.
$xhprof_runs = new XHProfRuns_Default();
// save the run under a namespace "xhprof_foo"
$run_id = $xhprof_runs->save_run($xhprof_data, "xhprof_foo");
echo '<a href="http://127.0.0.1/xhprof_html/index.php?run=' . $run_id . '&source=xhprof_foo" target="_blank">查看</a>';
}
}
这一段是整套接入里更关键的部分,流程可以拆成四步:
- 调用
xhprof_disable()结束分析并拿到原始性能数据; - 通过
$XHPROF_ROOT定位 XHProf 安装目录; - 引入
xhprof_lib.php和xhprof_runs.php; - 用
XHProfRuns_Default()保存数据,并返回一个$run_id。
最后输出的链接里,真正有用的是两个参数:
run:当前这次分析对应的唯一 ID;source=xhprof_foo:保存数据时使用的命名空间。
也就是说,只要请求执行完毕,XHProf 就会生成一条可追踪的分析记录,你可以直接点“查看”进入结果页,定位到底是哪个函数、哪段调用链更耗时。
这里还有两个实操层面的注意点。第一,$XHPROF_ROOT 必须和你实际部署的 XHProf 目录一致,否则库文件无法引入。第二,示例里直接 echo 出一个链接,更适合本地开发环境;如果是接口返回 JSON、Ajax 请求或其他严格响应格式,这种输出方式可能会干扰原始响应内容。
结果会保存到哪里,如何查看分析页面
完成以上配置后,项目里的所有请求都会生成性能分析数据。文中的做法给出了两种查看思路:
- 请求结束后,直接点击输出的“查看”链接;
- 进入 XHProf 的查看页面,按生成结果进行分析。
文中提到,可以通过 http://您的域名/xhprof/xhprof_html/index.php 查看分析结果。配合前面生成的 run_id 和 source 参数,就能打开某一次请求的完整性能报告。
从效果上看,这种接入方式适合做全请求级别的性能观察:不需要侵入业务逻辑,也不需要逐个方法手工包裹。你只要把开始和结束行为挂到合适的钩子上,ThinkPHP5 就能在每次请求生命周期内自动完成一次性能采样。
如果你的目标是快速定位慢请求、查看函数调用耗时分布,或者对比改动前后的执行差异,这种方案会比临时打印日志更直观;但如果用于高频生产流量,仍然要先评估额外开销,并考虑只对特定环境或指定请求开启分析。













