在高併發或多執行緒環境下,直接寫入檔案易造成資料競爭與損毀。本文提供一套「暫存檔 + 時間戳比對」機制,確保寫入原子性與資料一致性。
function mfopen($file, $mode='w+') {
$tempfile = generateTempfile('./tempdir', $file);
preg_match('/b/i', $mode) || ($mode .= 'b'); // 建議加入二進位模式
if (preg_match('/\w|a/i', $mode) && !is_writable($file)) {
exit("{$file} is not writable!");
}
$filemtime = $filemtime2 = 0;
$tempdir = dirname($tempfile);
is_dir($tempdir) || mkdir($tempdir, 0777);
// 確保複製過程中來源檔案未被修改
do {
clearstatcache();
$filemtime = filemtime($file);
copy($file, $tempfile);
$filemtime2 = filemtime($file);
} while (($filemtime2 - $filemtime) != 0);
if (!$handler = fopen($tempfile, $mode)) {
exit('Fail on opening tempfile, write authentication is must on temporary dir!');
}
return array(
0 => $handler,
1 => $filemtime,
2 => $file,
3 => $tempfile,
4 => $mode
);
}
function mfwrite(&$handler, $str='') {
if (strlen($str) > 0) {
$num = fwrite($handler[0], $str);
fflush($handler[0]);
}
clearstatcache();
$mtime = filemtime($handler[2]);
if ($mtime == $handler[1]) {
// 來源檔未變更,將暫存檔覆寫回來源
if ($num > 0) {
copy($handler[3], $handler[2]) || exit;
$handler[1] = filemtime($handler[3]);
touch($handler[2], $handler[1], $handler[1]);
}
} else {
// 來源檔已被他人修改,重新載入至暫存檔
copy($handler[2], $handler[3]) || exit;
touch($handler[3], $mtime, $mtime);
$handler[1] = $mtime;
}
}
function generateTempfile($tempdir='tempdir', $file) {
$rand = md5(microtime());
return "{$tempdir}/{$rand}_".$file;
}
$handler = mfopen($file, 'a+');
mfwrite($handler, $str);
作者註記 mfwrite 尚有優化空間,實際部署建議加入鎖機制或使用資料庫交易替代檔案操作。