用PHP读写JSON文件存收藏列表最直接,需检查文件存在性、处理null解码结果、加LOCK_EX防并发,路径用相对项目根目录的统一格式并归一化分隔符,操作走POST+CSRF保护。

用 PHP 读写 JSON 文件存收藏列表最直接
不需要数据库,小项目或本地工具场景下,把收藏数据存在 favorites.json 里是最轻量、最可控的方式。PHP 自带 json_encode() 和 json_decode(),配合 file_get_contents() 和 file_put_contents() 就能闭环操作。
常见错误是没处理文件不存在或权限问题:第一次运行时 favorites.json 为空或根本不存在,json_decode(file_get_contents('favorites.json')) 会返回 null,直接继续操作就会报 Warning: Invalid argument supplied for foreach() 这类错误。
- 初始化前先检查文件是否存在,不存在就创建空数组:
file_put_contents('favorites.json', json_encode([])) - 读取后务必判断返回值:
$data = json_decode(file_get_contents('favorites.json'), true); if ($data === null) { $data = []; } - 写入时加
LOCK_EX防并发覆盖:file_put_contents('favorites.json', json_encode($data), LOCK_EX)
收藏/取消收藏用文件路径做唯一键最稳妥
用户点击“收藏”按钮时,传过来的通常是文件路径(比如 /uploads/report_2024.pdf)或 ID。别用文件名(如 report.pdf)当 key——同名文件可能在不同目录;也别用完整绝对路径(如 /var/www/html/uploads/...),部署迁移后路径失效。
推荐用相对于项目根目录的路径,比如统一以 uploads/ 开头的相对路径,既稳定又可读。后端收到请求后,只校验路径格式(如是否含 ..、是否在允许目录内),不实际检查文件是否存在——收藏的是“意图”,不是“实时状态”。
立即学习“PHP免费学习笔记(深入)”;
- 添加收藏:
if (!in_array($path, $data)) { $data[] = $path; } - 取消收藏:
$data = array_filter($data, function($p) use ($path) { return $p !== $path; }); - 去重保存:
$data = array_values(array_unique($data));(避免重复点击导致冗余)
前端传参建议用 POST + CSRF 保护,别走 GET
收藏操作本质是状态变更,用 GET 请求不仅语义错误,还容易被爬虫或浏览器预加载触发误操作。必须走 POST,且带上简单 CSRF token(比如 session 里存一个随机字符串,表单隐藏域提交比对)。
错误做法是把路径拼在 URL 里:/favorite.php?action=add&path=uploads%2Fabc.pdf —— 这等于把收藏逻辑暴露给任何人,还能被反复提交。
- 表单 method 设为
post,action 指向favorite.php - 服务端验证
$_SERVER['REQUEST_METHOD'] === 'POST'和$_POST['token'] === $_SESSION['csrf_token'] - 路径参数从
$_POST['path']取,用basename()和dirname()做基础校验,禁止../回溯
注意 Windows 和 Linux 路径分隔符兼容性
如果用户上传的路径来自 Windows(用反斜杠 ),而服务器是 Linux,直接存进去会导致后续匹配失败——uploadsile.pdf ≠ uploads/file.pdf。别依赖前端传来的原始格式。
统一转成正斜杠,并标准化开头:$path = str_replace('\', '/', trim($path, '/')); $path = 'uploads/' . ltrim($path, 'uploads/');。这样无论传进来是 uploadsbc.pdf、abc.pdf 还是 /uploads/abc.pdf,最终都归一为 uploads/abc.pdf。
- 永远用
str_replace('\', '/', $path)归一化分隔符 - 避免用
realpath()—— 它依赖真实文件存在,而收藏不要求文件当前可达 - 如果未来要支持多级目录收藏(比如收藏整个
uploads/2024/),需额外判断末尾是否带/并区别处理



















