symfony缓存必须调用getitem()→set()→save()三步,键名需转义点斜杠为下划线,文件缓存需手动配置且依赖filesystem包,expiresafter()比expiresat()更安全。

直接调用 $cache->set('key', 'value') 会报错——CacheInterface 没这个方法。必须走 getItem() → set() → save() 三步,否则缓存根本没写进去。
缓存写入必须先 getItem(),不能直写
很多人把 Symfony 缓存当成键值对容器,写成 $cache->set('user_123', $data),结果抛出 Call to undefined method。这是因为 Symfony 的 PSR-6 实现要求显式获取 CacheItem 对象:
-
getItem('user_123')返回一个带状态的CacheItem实例,不是原始值 - 修改内容必须调用
$item->set($data),不能直接赋值给属性 -
save()才真正落盘;漏掉这一步,前面所有操作都无效 - 用
ArrayAdapter测试时,save()后还得getItem()->isHit()验证是否生效
文件缓存在 dev 环境默认不启用,需手动配置
Symfony 5.4+ 在 dev 环境下默认用 ArrayAdapter(内存型),哪怕你改了 cache.yaml,var/cache/dev/ 下也始终为空。这不是 bug,是设计行为。
- 要让文件缓存真正跑起来,必须在
config/packages/cache.yaml显式指定:framework:cache:app: cache.adapter.filesystemdefault_psr6_provider: 'cache.provider.filesystem' - 依赖
symfony/filesystem包,缺了会报Class not found - 目录权限问题常导致
Failed to write cache file,开发时可用chmod -R 777 var/cache快速排除(上线禁用) - 服务定义里若用
cache.pool类型,记得加public: true,否则 DI 容器拒绝get()
expiresAfter() 比 expiresAt() 更安全,Redis 会截断小数秒
expiresAfter(3600) 是秒,expiresAt(new \DateTime('+1 hour')) 是绝对时间——但传 new \DateTime('+3600 seconds') 极易翻车:DateTime 解析字符串存在毫秒级偏差,Symfony 内部比较逻辑可能判为“已过期”,导致缓存 1 秒就失效。
- 优先用
expiresAfter():语义清晰、无时区陷阱、兼容所有后端 - 非用
expiresAt()不可时,传new \DateTimeImmutable('+1 hour'),避免意外修改 - Redis 后端会把过期时间强制截断为整秒,
3600.5变成3600,别指望毫秒控制 - 测试时用
var_dump($item->getExpiresAt())看真实值,别只信自己写的字符串
键名含 . 或 / 会触发 PSR-6 兼容性问题
像 user.profile.name 或 api/v1/users 这类含点号或斜杠的键名,在部分适配器(尤其是某些文件系统实现)中会被误解析为路径分隔符,导致缓存项无法命中或写入失败。
- 统一替换为下划线:
strtr($key, ['.' => '_', '/' => '_']) - 这不是 Symfony 特有规则,而是 PSR-6 规范对键名的宽松要求带来的实际兼容问题
- 即使当前环境能跑通,换 Redis 或 APCu 后可能突然失效,建议从一开始就规范化
最常被忽略的是缓存键的命名规范和 save() 的必要性——前者影响跨环境一致性,后者决定缓存是否真的存在。这两点不出问题,其他优化才有意义。











