在php中打开文件,读取内容,然后使用基于原始内容的输出覆盖文件内容的最干净的方法是什么?具体来说,我试图打开一个用项目列表填充的文件(以换行符分隔),处理/添加项目到列表中,从列表中删除最老的N个条目,最后将列表写回文件。
fopen(<path>, 'a+')
flock(<handle>, LOCK_EX)
fread(<handle>, filesize(<path>))
// process contents and remove old entries
fwrite(<handle>, <contents>)
flock(<handle>, LOCK_UN)
fclose(<handle>)
注意,我需要用flock()锁定文件,以便在多个页面请求中保护它。当fopen()执行时,"w+"标志会起作用吗?php手册声明它将截断文件到零长度,所以看起来可能会阻止我读取文件的当前内容。
如果文件不是太大(也就是说,您可以确信加载它不会超出PHP的内存限制),那么最简单的方法是将整个文件读入字符串(file_get_contents()
),处理字符串,并将结果写回文件(file_put_contents()
)。这种方法有两个问题:
- 如果文件太大(比如,几十或几百兆字节),或者处理需要内存,你将会耗尽内存(当你有多个实例运行时更是如此)。
- 操作是破坏性的;当保存中途失败时,您将丢失所有原始数据。
如果存在上述任何一种问题,则B计划是处理文件并同时写入临时文件;成功完成后,关闭两个文件,重命名(或删除)原始文件,然后将临时文件重命名为原始文件名。
阅读
$data = file_get_contents($filename);
写file_put_contents($filename, $data);
一个解决方案是使用一个单独的锁文件来控制访问。
此解决方案假设只有您的脚本,或您有权访问的脚本,想要写入该文件。这是因为脚本需要知道检查一个单独的文件是否可以访问。
$file_lock = obtain_file_lock();
if ($file_lock) {
$old_information = file_get_contents('/path/to/main/file');
$new_information = update_information_somehow($old_information);
file_put_contents('/path/to/main/file', $new_information);
release_file_lock($file_lock);
}
function obtain_file_lock() {
$attempts = 10;
// There are probably better ways of dealing with waiting for a file
// lock but this shows the principle of dealing with the original
// question.
for ($ii = 0; $ii < $attempts; $ii++) {
$lock_file = fopen('/path/to/lock/file', 'r'); //only need read access
if (flock($lock_file, LOCK_EX)) {
return $lock_file;
} else {
//give time for other process to release lock
usleep(100000); //0.1 seconds
}
}
//This is only reached if all attempts fail.
//Error code here for dealing with that eventuality.
}
function release_file_lock($lock_file) {
flock($lock_file, LOCK_UN);
fclose($lock_file);
}
这将防止并发运行的脚本读取旧信息并更新旧信息,从而导致您丢失在读取文件后另一个脚本更新的信息。它将只允许脚本的一个实例读取文件,然后用更新的信息覆盖它。
虽然这有望回答最初的问题,但它并没有给出一个好的解决方案,以确保所有并发脚本最终都能够记录它们的信息。