only array cache can be push
分享
收藏
评论
作者 🐼
chen
-
网站报错only array cache can be push,定位到是common.php 缓存数据格式污染导致的,


这是一个典型的 **缓存数据类型不匹配** 导致的运行时错误。错误提示 `only array cache can be push` 明确表示:程序在执行缓存写入/追加操作时,期望接收的是数组类型,但实际读取到的缓存值已被污染成其他类型(如字符串、整型、null、序列化对象或 Redis 的非 List 类型)。
由于我无法直接查看你提供的截图,以下提供一套标准且可落地的排查与修复方案,适用于大多数 PHP 后端(包括 EasyAdmin8 常见底层框架)。
---
### 🔍 一、问题根因分析
| 可能原因 | 说明 |
|:---|:---|
| **历史异常残留** | 某次请求失败未处理返回值,导致非数组数据覆盖了原本应为数组的缓存键 |
| **手动改库/删表/清空插件** | 直接修改了数据库或缓存文件,破坏原有结构 |
| **反序列化/JSON 解析未判空** | `json_decode()` 或 `unserialize()` 失败返回 `null`,后续未做类型回退 |
| **多端并发写入冲突** | 多个进程同时读写同一缓存键,中间状态被错误持久化 |
| **Redis 类型混用** | 同一 Key 曾被当 String 存过,后被当 List/Hash 使用,客户端校验拦截 |
---
### 🛠️ 二、快速恢复方案(优先执行)
1. **定位具体缓存 Key**
在 `common.php` 报错行上方临时加入:
```php
\Log::debug('Cache debug', [
'key' => $your_cache_key,
'type' => gettype($current_cache_value),
'raw' => var_export($current_cache_value, true),
'trace' => debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 3)
]);
```
2. **清理脏缓存**
- **Redis**:`redis-cli DEL <缓存key>` 或使用框架命令 `php think cache:clear` / `php artisan cache:clear`
- **File**:删除 `runtime/cache/` 或对应目录下的过期/损坏文件
- **后台**:通过 EasyAdmin 系统设置 → 缓存管理 → 一键清理
3. **重启服务**(如有 OPcache/Swoole/FPM)
---
### 💻 三、代码层根治建议(修改 `common.php`)
#### ✅ 1. 安全读取 + 类型兜底
```php
// 原代码可能类似:$data = cache()->get('xxx');
$data = cache_get('xxx_key');
// 增加类型保护,污染时自动重置为数组
if (!is_array($data)) {
\Log::warning("Cache format corrupted for key xxx_key, resetting to [].");
$data = [];
// 可选:从源头重新初始化默认数据
$data = load_default_array_data();
}
```
#### ✅ 2. 安全 Push/追加逻辑
```php
// 确保始终是数组再追加
$cacheValue = is_array($cacheValue) ? $cacheValue : [];
$newItem = ['id' => 1, 'status' => 1];
$cacheValue[] = $newItem;
// 重新写入(注意 TTL 保持一致)
cache_set('xxx_key', $cacheValue, 3600);
```
#### ✅ 3. 避免直接覆盖已存在的 Key
如果你是在做 `list` 或 `stack` 类型的缓存,务必确认目标 Key 之前没有被写入过非数组值:
```php
// ❌ 危险写法
cache_put('user_list', json_encode(['name'=>'test']));
// ✅ 正确做法
$current = cache_get('user_list') ?: [];
if (!is_array($current)) $current = [];
$current[] = ['name'=>'test'];
cache_put('user_list', $current);
```
---
### 🧭 四、排查 Checklist
- [ ] 打开 `common.php`,找到触发 `push`/写入的精确行号
- [ ] 检查该行之前是否有 `return ''`、`die()`、或数据库查询未判空直接赋值
- [ ] 确认该缓存 Key 是否被其他插件/脚本单独使用(键名冲突)
- [ ] 查看 Web 服务器/应用日志,寻找第一次出现脏数据的请求时间点
- [ ] 如果是 Redis,执行 `TYPE your_cache_key` 确认当前底层数据结构
---
### 📦 五、预防最佳实践
1. **统一缓存入口封装**:不要频繁调用原生 `cache()`/`Cache::`,建议在公共类中提供 `SafeCache::get/set/push`
2. **强类型约定**:团队规范规定数组类缓存必须以 `_arr` 后缀结尾,便于肉眼审查
3. **加数据指纹校验**(可选):
```php
$payload = [
'data' => $arrayValue,
'_v' => md5(json_encode($arrayValue))
];
cache_set($key, $payload);
```
4. **监控告警**:对核心缓存键设置定时校验任务,发现类型偏差自动预警或自愈
---
如果你能提供 `common.php` 中报错行前后的 **3~5 行代码**(注意脱敏敏感信息),我可以给出更精确的重构片段。通常只需加上 `is_array()` 判断+容错重置,即可彻底杜绝此类问题复发。
⬆️