我有一个文本文件,多个用户将同时编辑该文本文件(每个用户每次编辑都限于一行)。我已经在StackOverflow.com上找到了所需功能的“行编辑”部分的解决方案,特别是@Gnarf在以下问题中提供的第四个解决方案(用于大文件):
how to replace a particular line in a text file using php?
基本上,它将整个文件内容重写为新的临时文件(包括用户的编辑),然后在完成后将临时文件重命名为原始文件。太好了!
为了避免一个用户的编辑与另一个用户的编辑同时发生冲突,我引入了flock()功能,如我在下面代码中的变体所示:
$reading = fopen($file, 'r');
$writing = fopen($temp, 'w');
$replaced = false;
if ((flock($reading, LOCK_EX)) and (flock($writing, LOCK_EX))) {
echo 'Lock acquired.<br>';
while (!feof($reading)) {
$line = fgets($reading);
$values = explode("|",$line);
if ($values[0] == $id) {
$line = $id."|comment edited!".PHP_EOL;
$replaced = true;
}
fputs($writing, $line);
}
flock($reading, LOCK_UN);
flock($writing, LOCK_UN);
fclose($reading);
fclose($writing);
} else {
echo 'Lock not acquired.<br>';
}
我已经确保$ temp文件始终具有唯一的文件名。此处的完整代码:https://pastebin.com/E31hR9Mz
我知道flock()会强制脚本的任何其他执行在队列中等待,直到第一次执行完成且flock()已释放。到目前为止,一切都很好。
但是,问题开始于脚本的末尾,这是时候到了要重命名()临时文件来替换原始文件的时候了。
if ($replaced) {
rename($temp, $file);
} else {
unlink($temp);
}
从我所看到的情况来看,如果原始文件仍然具有flock(),则rename()将失败,因此我需要在此之前释放flock()。但是,我还需要它保持锁定状态,否则,一旦释放了先前的flock(),运行同一脚本的另一个用户立即打开新的flock()时,rename()就会失败。发生这种情况时,它将返回:
Warning: rename(temporary.txt,original.txt): Access is denied. (code: 5)
tl; dr::我似乎有点赶上Catch-22了。看起来rename()在锁定的文件上不起作用,但是解锁文件将使另一个用户可以在rename()发生之前立即再次将其锁定。
有什么想法吗?
更新:在对flock()的工作方式进行了广泛的研究之后,(以外行的说法,不能保证另一个脚本会尊重“ lock”,因此,它实际上不是“锁定”一词,因为这个词的字面意思就是这样)。我选择了这种解决方案,它的工作原理就像一个魅力:
https://docstore.mik.ua/orelly/webprog/pcook/ch18_25.htm
锁定冒险中的“好锁”。