因為
1) 如果多個 PHP 會話同時鎖定,flock() 是不安全的。
2) 如果多個 PHP 會話同時附加,fopen( , 'a') 是不安全的。
3) usleep 和 sleep 因存在問題而聞名。
我編寫了 Weblog 函數,其目的是將一行附加到記錄中。此函數處理並發的方式如下
- 嘗試建立一個名為:$directory . date('Ymd') . $logfile . 1 . lock 的鎖定檔案
- 如果失敗,嘗試建立一個名為:$directory . date('Ymd') . $logfile . 2 . lock 的鎖定檔案
- 等等。嘗試 100 次後返回 false。
- 當取得鎖定檔案後,開啟或建立名為:$directory.date('Ymd').$logfile.1 (或 .2 或 .3 或 .25)的檔案。
- 如果建立,則會寫入「延伸記錄檔」標頭。
- 寫出該行。
- 關閉檔案,如果建立,則設定正確的存取權限。(我在網頁伺服器上建立檔案時遇到一些問題,當我開啟到網頁目錄的 FTP 會話時,我看不到它們。chmod 對我來說很有效)。
- 移除鎖定檔案。
只有一個缺點,會建立多個記錄檔。
例如(於 2010 年 9 月 15 日執行)
Weblog('./' , 'visit', '有人請求了首頁');
可能會導致 100 個檔案,具體取決於有多少並行的 PHP 會話同時嘗試附加記錄行
./20100915visit.1
./20100915visit.2
./20100915visit.3
./20100915visit.4
...
./20100915visit.100
此函數貢獻給公眾領域。也許您可以留下作者註釋來給我一些功勞,但這不是必需的。您可以隨意修改它。
(此函數的靈感來自 Kuzma dot Deretuke at gmail dot com 提出的函數 m_lock_file)
<?php
function Weblog($directory, $logfile, $message)
{
$curtime = time();
$logfile = date('Ymd',$curtime) . $logfile;
if (!isset($directory) || $directory === false)
$directory = './';
if (substr($directory,-1) !== '/')
$directory = $directory . '/';
$count = 1;
while(1)
{
$logfilename = $directory . $logfile . '.' . $count;
$lockfile = $logfilename . '.lock';
$lockhandle = false;
if (!file_exists($lockfile) || @unlink($lockfile))
$lockhandle = @fopen($lockfile, 'xb');
if ($lockhandle !== false) break;
$count++;
if ($count > 100) return false;
}
if (file_exists($logfilename))
{
$created = false;
$loghandle = @fopen($logfilename, 'ab');
}
else
{
$loghandle = @fopen($logfilename, 'xb');
if ($loghandle !== false)
{
$created = true;
$str = '#version: 1.0' . "\r\n" .
'#Fields: date time c-ip x-msg' . "\r\n";
fwrite($loghandle,$str);
}
}
if ($loghandle !== false)
{
$str = date('Y-m-d',$curtime) . "\t" .
date('H:i:s', $curtime) . "\t" .
(isset($_SERVER['REMOTE_ADDR']) ? $_SERVER['REMOTE_ADDR'] : '-') . "\t" .
'"' . str_replace('"', '""', $message) . '"' . "\r\n";
fwrite($loghandle,$str);
fclose($loghandle);
if ($created) chmod($logfilename,0644); $result = true;
}
else
{
$result = false;
}
fclose($lockhandle);
@unlink($lockfile);
return $result;
}
?>