准备自己实现一个基于文件的简单缓存类,用于一些小外包项目。原本只是打算按照特定的JSON格式进行存储,然后用很方便的两个函数 file_get_content
和 file_put_content
进行读写
后来想给缓存类加个自增/减的方法,用来做简单的统计之类。如果考虑到瞬间并发情况,为了防止两个请求互相干扰,就必须得上文件锁了
首先是设置缓存,这个没啥难度:
- 首先一个
$h = fopen($path, 'w+b')
打开文件 - 然后
flock($h, LOCK_EX)
锁定它,防止两次并发请求同时读写 - 接着
fwrite($h, 'xxx...')
写入内容 - 继续
flock($h, LOCK_UN)
解锁文件 - 最后
fclose($h)
关闭文件指针
然后是读取缓存,按照上面流程如法炮制。只是把第3步的 fwrite
换成 fread
,把读出来的内容解析一下返回。看起来好像没什么问题。
然而我无论怎么试,缓存总是能正常写入,却无法读取
这我就懵逼了。经过一阵调试,发现 fread
总是返回空,这是为毛!?
再一阵调试过后,我发现在 fopen
之后,文件就已经变成空了。这就很奇怪了
我打开官方文档,仔细地再次阅读了w+
模式的说明
读写方式打开,将文件指针指向文件头并将文件大小截为零。如果文件不存在则尝试创建之。
沃日感情你所谓的将文件大小截为零就是会清空文件内容的意思啊?这表达得也太隐晦了吧?
看来w+是不能用了。我试着把fopen的模式切换到 r+
、x+
、a+
以及 c+
,都无法达到先读取文件内容,然后覆盖写入的效果
中间折腾过程略……
后来发现一个函数 ftruncate
,文档中描述为将文件截断到给定的长度。我用 r+b
打开、锁定文件,读取内容后用该函数将其清空;递增/减数值后写入,解锁。看起来一切都那么顺利
然而我最终保存的文件,在正常内容前有一串奇怪的空字符,导致再次读取时格式不对
怀疑是清空内容后没有将指针复位。找到一个 rewind
函数,执行后再进行写入操作。最终一切正常,可喜可贺、可喜可贺!
学习了