PHP使用LOCK_EX读写JSON文件时出现损坏及json_decode失败问题排查
问题分析与修复:PHP修改JSON文件时的损坏与解析失败问题
嘿,我来帮你排查这个问题——你的代码第一次运行正常,但后续调用就出问题了,核心问题出在文件指针位置处理和不完整的内容读取上,具体拆解如下:
核心问题拆解
fgets仅读取单行内容:你的初始JSON是单行的,fgets刚好能读完整,但如果后续写入的JSON变成多行(比如不小心开启了JSON_PRETTY_PRINT),或文件混入换行符,fgets只会读取第一行,拿到不完整的JSON字符串,直接导致json_decode返回false。- 文件指针未重置,写入产生空字节:这是最致命的点:
- 用
r+模式打开文件,第一次读取fgets后,指针已经移动到文件末尾; ftruncate($fh, 0)会把文件截断到0长度,但PHP的ftruncate不会改变指针位置,指针仍停留在原文件末尾;- 此时调用
fwrite,会从指针当前位置开始写入,导致文件开头出现大量空字节(\0); - 第二次调用代码时,
fgets读取到带空字节的字符串,json_decode无法解析,后续修改写入会彻底破坏文件。
- 用
- 缺少错误检查:没有对
fopen、flock、json_decode等关键操作的返回值做校验,出错后继续执行,进一步加剧文件损坏。
修复后的代码
我调整了代码,解决了上述所有问题,还加了必要的错误处理:
<?php define('PATH_TO_FILE', './test.json'); $fh = fopen(PATH_TO_FILE, "r+"); if (!$fh) { die("无法打开目标文件"); } // 确保获取排他锁,处理锁获取失败的情况 if (!flock($fh, LOCK_EX)) { fclose($fh); die("无法获取文件排他锁,请稍后重试"); } // 重置指针到文件开头,读取完整内容 fseek($fh, 0); $data = fread($fh, filesize(PATH_TO_FILE)); $jsonData = json_decode($data, true); // 检查JSON解析是否成功 if ($jsonData === null) { flock($fh, LOCK_UN); fclose($fh); die("JSON解析失败,文件内容可能已损坏"); } // 修改数据 $jsonData['mod_1'] = 1; $dataWrite = json_encode($jsonData); // 重置指针到开头,截断文件后写入 fseek($fh, 0); if (!ftruncate($fh, 0)) { flock($fh, LOCK_UN); fclose($fh); die("无法截断文件"); } fwrite($fh, $dataWrite); // 强制将缓冲区内容写入磁盘,避免数据丢失 fflush($fh); // 释放锁并关闭文件 flock($fh, LOCK_UN); fclose($fh); clearstatcache(); ?>
修复点说明
- 读取完整文件内容:用
fread配合filesize读取整个文件,代替fgets,确保拿到完整的JSON字符串,不受换行影响。 - 强制重置文件指针:每次读取、写入前都用
fseek($fh, 0)把指针定位到文件开头,彻底避免指针位置错误导致的空字节问题。 - 添加错误检查:对每个关键操作的返回值做判断,一旦出错立即释放锁、关闭文件并终止执行,防止进一步破坏文件。
- 添加
fflush:强制将缓冲区内容写入磁盘,避免因程序意外退出导致数据丢失。
内容的提问来源于stack exchange,提问作者user2624744
相关产品推荐
相关产品推荐

