Files
YwxAppThink/ywxapp/middleware/AddonPerformanceMiddleware.php

80 lines
2.8 KiB
PHP
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<?php
// +----------------------------------------------------------------------
// | YwxApp [ WE CAN DO IT JUST THINK ]
// +----------------------------------------------------------------------
// | Copyright (c) 2026-2036 http://ywxapp.cn All rights reserved.
// +----------------------------------------------------------------------
// | Author: ywxapp<admin@ywxapp.cn>
// +----------------------------------------------------------------------
declare(strict_types=1);
namespace ywxapp\middleware;
use think\facade\Config;
use ywxapp\service\AddonPerformanceMonitor;
/**
* 插件性能监控中间件(全局注册于 app/middleware.php
*
* 设计要点:
* - 从请求路径首段解析插件名(插件路由统一为 /<插件名>/<group>/...),
* 仅对「已启用插件」的请求做性能采样,核心应用(admin/index/api 等)零开销;
* - 用 AddonPerformanceMonitor::measure() 包裹 $next($request),把每次插件
* HTTP 请求的真实耗时/内存/成功失败写入缓存,使 addon:health 的性能项有真数据;
* - measure() 的 finally 已防御性吞掉缓存异常,本中间件无需再包 try/catch,
* 且绝不二次调用 $next(避免重复执行业务);
* - 控制器抛异常时 measure() 记录为失败并原样向外抛出,框架错误处理不受影响。
*
* @author ywxapp <admin@ywxapp.cn>
*/
class AddonPerformanceMiddleware
{
public function handle($request, \Closure $next)
{
$addon = $this->resolveAddon($request);
if ($addon === null) {
return $next($request);
}
$action = $this->resolveAction($request);
return AddonPerformanceMonitor::measure($addon, $action, function () use ($next, $request) {
return $next($request);
});
}
/**
* 从路径首段解析插件名,并确认其为已启用插件(Config::get('addon') 仅含 state=1
*/
private function resolveAddon($request): ?string
{
$path = trim((string) $request->pathinfo(), '/');
if ($path === '') {
return null;
}
$segments = explode('/', $path);
$maybe = $segments[0];
$addon = Config::get('addon', []);
foreach ($addon as $info) {
if (($info['name'] ?? '') === $maybe) {
return $maybe;
}
}
return null;
}
/**
* 动作标签:HTTP 方法 + 插件名之后的剩余路径(如 GET backend/addonreview/index
*/
private function resolveAction($request): string
{
$path = trim((string) $request->pathinfo(), '/');
$segments = explode('/', $path);
array_shift($segments); // 去掉插件名首段
$rest = implode('/', $segments);
return ($request->method() ?? 'GET') . ' ' . ($rest === '' ? 'index' : $rest);
}
}