hyperf 的 aop 仅能拦截由 di 容器创建且被 @aspect 和 @around 注解标记的类方法,无法拦截 var_dump、echo 或静态日志门面调用;需确保切面类在扫描路径内、已生成代理类,并在 @around 中显式调用 $proceedingjoinpoint->process()。

Hyperf 的 AOP 不能直接拦截 var_dump、echo 或日志门面调用(如 Logger::info()),因为这些不是容器管理的类方法调用——AOP 只对通过 DI 容器解析并代理的类实例生效。
哪些日志操作能被 AOP 拦截?
只有明确由容器创建、且目标方法被 @Aspect 和 @Around 注解覆盖的类方法,才能触发切面逻辑。比如你封装了一个 OrderService,其中 createOrder() 方法内部调用了 $this->logger->info(),此时 AOP 可以拦截 createOrder() 入口/出口,但无法拦截那行 $this->logger->info() 本身。
- AOP 生效前提是:目标类由
Container实例化,且启用了代理(默认开启) - 日志门面(
Logger::info())是静态调用,不走对象代理,AOP 无感知 - 直接 new 出来的对象(如
new OrderService())绕过容器,AOP 不生效 - 想统一打日志,应把日志逻辑写在被代理的方法里,或用
@Before/@After在方法边界注入
如何用 @Around 给业务方法自动加日志?
核心是捕获方法执行前后的上下文,并控制是否继续执行。注意 ProceedingJoinPoint 必须显式调用 $proceedingJoinPoint->process(),否则原方法不会运行。
#[Aspect]
#[PointBean(OrderService::class)]
class LogAspect
{
#[Around]
public function log(ProceedingJoinPoint $proceedingJoinPoint): mixed
{
$method = $proceedingJoinPoint->getMethodName();
$params = $proceedingJoinPoint->getArguments();
Logger::info("【START】{$method}", ['params' => $params]);
try {
$result = $proceedingJoinPoint->process();
Logger::info("【END】{$method}", ['result' => is_object($result) ? get_class($result) : gettype($result)]);
return $result;
} catch (\Throwable $e) {
Logger::error("【ERROR】{$method}", [
'message' => $e->getMessage(),
'file' => $e->getFile(),
'line' => $e->getLine()
]);
throw $e;
}
}
}
- 不要在
log()中漏掉$proceedingJoinPoint->process(),否则业务逻辑静默丢失 - 捕获
\Throwable而非仅\Exception,避免 PHP 致命错误(如Fatal error: Allowed memory size exhausted)逃逸 - 日志中避免直接
json_encode($params),可能触发不可序列化对象的报错;建议用var_export($params, true)或白名单字段提取
为什么 Logger::info() 调用没进切面,但 LogAspect 还是没日志?
常见原因是切面未被扫描加载,或注解未启用。Hyperf 默认只扫描 app/ 目录,而 Aspect 类若放在 app/Aspect/ 外(比如 app/Utils/LogAspect.php),即使加了 #[Aspect] 也不会注册。
- 确认
config/autoload/annotations.php中scan配置包含你的切面路径,例如:'scan' => ['paths' => ['app', 'app/Aspect']] - 检查
composer.json的autoload是否已运行composer dump-autoload,否则新类不被识别 - 运行
php bin/hyperf.php di:scan强制重新生成代理类(尤其修改注解后) - 切面类必须有命名空间,且不能是
anonymous class或闭包
真正难的不是写切面,而是判断某段日志该不该放进去——比如幂等校验失败时打 warn,还是让上游重试;又比如敏感参数(手机号、token)要不要脱敏再进日志。这些决策不会因 AOP 自动发生,得靠你在 log() 方法里手动处理。











