先说一个核心判断:ThinkPHP 的事件机制本身不会替你清缓存,它没有内置“事件清缓存”这种一目了然的开关。所谓“通过事件清缓存”,本质上是在监听器里手动写清除逻辑。别被名字带偏——重点不是事件怎么“自动”清,而是你得搞清楚:Cache::clear() 能不能直接用?清哪一部分?该不该执行 php think clear?

事件监听器里调用 Cache::clear() 的实际写法
缓存清理的动作是写在监听器里的(比如 app/event/Listen/UserUpdated.php),不是靠事件注册自动完成的。注意几点:
- 必须显式引入门面:
use think\facade\Cache; - 千万别只写一句
Cache::clear();—— 这会把所有缓存全部清空,包括其他模块正在用的配置缓存或路由缓存,轻则页面 404,重则 Class not found。 - 推荐按标签清除:比如用户更新后只清用户相关数据,
Cache::tag('user')->clear();—— 前提是之前存数据时用了Cache::tag('user')->set(...)。 - 如果用的是模型查询缓存(非 tag),就得自己构造一致的 key,然后
Cache::delete($key)。否则clear()会误伤无关数据。
php think clear 能不能塞进事件监听器里执行
不能。命令行指令 php think clear 是 CLI 环境专用的,监听器运行在 Web 请求上下文(Apache 或 FPM),没有 shell 执行权限,也找不到 think 入口文件路径。强行 exec('php think clear') 不仅会失败,还有安全隐患。
真正可行的替代方案只有两个:
- 在监听器里用 PHP 原生函数递归删除目录,例如
delDirAndFile(RUNTIME_PATH . 'cache');(注意:需要自己实现delDirAndFile函数,而且仅限文件驱动)。 - 把清理动作下沉到部署或定时任务中——比如用户更新后发一条消息到队列,由 worker 进程在 CLI 下执行
php think clear --cache。
TP6 事件中清 Redis 缓存的特殊注意事项
TP6 的 Cache::clear() 在 Redis 驱动下默认执行 FLUSHDB,影响的是整个 DB,不是按前缀过滤。这跟你预期的“只清 user 相关 key”往往对不上。
- 先确认
config/cache.php中 Redis 配置的prefix是否启用;没有设 prefix 时,tag()清理也会失效。 - 想精准删 key,得绕过门面,直接操作 Redis 实例:
Cache::store('redis')->handler()->keys('user_*'),再逐个del(注意:keys命令在生产 Redis 上要慎用,会影响性能)。 - TP6.3+ 支持
Cache::clear('', 'redis', ['prefix' => 'user_']),但这个参数官方文档里没有写,需要查源码确认是否可用。
为什么事件清缓存后页面仍显示旧数据
大概率不是缓存没清,而是清错了地方:
- 模板缓存(
runtime/temp/)没清,但事件里只清了cache目录 → 补上delDirAndFile(RUNTIME_PATH . 'temp'); - 用了 Swoole 或 RoadRunner,PHP 进程常驻,
opcache或apcu缓存了已加载的类或配置 → 光靠删 runtime 不管用,得 reload 进程或禁用 opcache。 - 前端浏览器或 CDN 缓存了响应 → 这和 ThinkPHP 无关,加响应头
Cache-Control: no-cache或改 URL 参数。
事件里的缓存清理是“最不可靠的一环”,它依赖你对缓存层级、驱动行为、进程模型的精确控制。线上环境建议用 php think optimize:route + php think optimize:config 配合部署脚本,而不是靠用户操作触发事件去擦屁股。