Files
laralog/app/Blog/Support/PageCacheMiddleware.php
ak 01861f8809 feat: 插件使用说明(usage)+ 页面缓存命中统计
- plugin.json 新增 usage 字段(多行文本),后台插件卡片折叠展示;四个内置插件补齐使用说明
- PageCacheMiddleware 记录命中/未命中计数(按日滚动),仪表盘新增「页面缓存」统计卡(条目数 + 命中率),用于判断前台缓存效果、是否需额外加速
- 文档与测试同步
2026-08-12 03:57:22 +08:00

133 lines
3.4 KiB
PHP

<?php
declare(strict_types=1);
namespace App\Blog\Support;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Cache;
use Symfony\Component\HttpFoundation\Response;
/**
* 前台页面响应缓存:
* - 仅缓存匿名 GET 请求的 HTML 页面(首页/列表/文章/归档/标签等)
* - 登录用户、后台、API、动态端点不缓存
* - 浏览量走独立的 /track-view 端点,不受整页缓存影响
*/
class PageCacheMiddleware
{
public const TAG = 'pages';
public function handle(Request $request, \Closure $next): Response
{
if (! $this->shouldCache($request)) {
return $next($request);
}
$key = $this->key($request);
$ttl = (int) config('blog.page_cache_ttl', 300);
if ($cached = Cache::get($key)) {
self::increment('hits');
return response($cached['body'], 200, [
'Content-Type' => $cached['type'],
'Cache-Control' => 'public, max-age='.$ttl,
]);
}
self::increment('misses');
$response = $next($request);
if ($response->getStatusCode() === 200) {
Cache::put($key, [
'body' => $response->getContent(),
'type' => $response->headers->get('Content-Type') ?: 'text/html; charset=utf-8',
], $ttl);
self::rememberKey($key);
}
return $response;
}
public static function flush(): void
{
foreach ((array) Cache::get(self::TAG.'_keys', []) as $key) {
Cache::forget($key);
}
Cache::forget(self::TAG.'_keys');
}
public static function count(): int
{
return count((array) Cache::get(self::TAG.'_keys', []));
}
/**
* 命中/未命中统计(按日滚动),用于判断前台缓存效果。
*/
public static function stats(): array
{
$hits = (int) Cache::get(self::TAG.'_hits', 0);
$misses = (int) Cache::get(self::TAG.'_misses', 0);
$total = $hits + $misses;
return [
'hits' => $hits,
'misses' => $misses,
'count' => self::count(),
'rate' => $total > 0 ? round($hits / $total * 100, 1) : 0.0,
];
}
public static function resetStats(): void
{
Cache::forget(self::TAG.'_hits');
Cache::forget(self::TAG.'_misses');
}
private static function increment(string $metric): void
{
$key = self::TAG.'_'.$metric;
Cache::put($key, (int) Cache::get($key, 0) + 1, now()->addDay());
}
private static function rememberKey(string $key): void
{
$keys = (array) Cache::get(self::TAG.'_keys', []);
$keys[$key] = true;
Cache::put(self::TAG.'_keys', $keys, now()->addDay());
}
private function shouldCache(Request $request): bool
{
if (! $request->isMethod('GET')) {
return false;
}
if (auth()->check()) {
return false;
}
$path = $request->path();
if (str_starts_with($path, 'admin') || str_starts_with($path, 'api') || str_starts_with($path, 'themes')) {
return false;
}
// 动态端点
if (str_starts_with($path, 'track-view')) {
return false;
}
return true;
}
private function key(Request $request): string
{
return self::TAG.':'.md5($request->fullUrl());
}
}