在處理服務器日誌文件時,我們經常會遇到格式不統一的問題。比如IP 地址可能有前導零、時間戳格式可能不一致、請求路徑可能帶有各種參數。為了統一處理這些字段, preg_replace_callback_array是一個非常強大的工具,它允許我們對多個正則表達式分別綁定不同的回調函數,在一次遍歷中完成複雜的替換邏輯。
本文將介紹如何使用preg_replace_callback_array來統一格式化日誌中的IP、時間字段。
假設我們有如下日誌內容(簡化版):
127.000.000.001 - - [21/Apr/2025:15:32:01 +0000] "GET /index.php?id=123 HTTP/1.1" 200
192.168.1.10 - - [21-Apr-2025 15:32:01] "POST /submit.php HTTP/1.1" 404
我們希望:
將IP 地址標準化(去除前導零);
將時間統一為YYYY-MM-DD HH:MM:SS格式;
可選:屏蔽掉路徑中的參數,例如/index.php?id=123 → /index.php
<?php
$log = <<<LOG
127.000.000.001 - - [21/Apr/2025:15:32:01 +0000] "GET /index.php?id=123 HTTP/1.1" 200
192.168.1.10 - - [21-Apr-2025 15:32:01] "POST /submit.php HTTP/1.1" 404
LOG;
// 定義正則與對應的處理回調
$patterns = [
// IP 地址格式化:去掉前導零
'/\b(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})\b/' => function ($matches) {
return implode('.', array_map('intval', array_slice($matches, 1, 4)));
},
// Apache 風格時間戳 [21/Apr/2025:15:32:01 +0000]
'/\[(\d{2})\/(\w{3})\/(\d{4}):(\d{2}):(\d{2}):(\d{2}) [+\-]\d{4}\]/' => function ($matches) {
$monthMap = [
'Jan' => '01', 'Feb' => '02', 'Mar' => '03', 'Apr' => '04',
'May' => '05', 'Jun' => '06', 'Jul' => '07', 'Aug' => '08',
'Sep' => '09', 'Oct' => '10', 'Nov' => '11', 'Dec' => '12'
];
return sprintf('%s-%s-%s %s:%s:%s',
$matches[3], // 年
$monthMap[$matches[2]] ?? '01', // 月
$matches[1], // 天
$matches[4], $matches[5], $matches[6]
);
},
// 天志格式中另一种时间 [21-Apr-2025 15:32:01]
'/(\d{2})-(\w{3})-(\d{4}) (\d{2}):(\d{2}):(\d{2})/' => function ($matches) {
$monthMap = [
'Jan' => '01', 'Feb' => '02', 'Mar' => '03', 'Apr' => '04',
'May' => '05', 'Jun' => '06', 'Jul' => '07', 'Aug' => '08',
'Sep' => '09', 'Oct' => '10', 'Nov' => '11', 'Dec' => '12'
];
return sprintf('%s-%s-%s %s:%s:%s',
$matches[3],
$monthMap[$matches[2]] ?? '01',
$matches[1],
$matches[4], $matches[5], $matches[6]
);
},
// 去掉 URL 參數(如 /index.php?id=123 → /index.php)
'#(GET|POST|PUT|DELETE|HEAD) (/[\w\-\/\.]+)(\?[^\s"]*)?#' => function ($matches) {
return $matches[1] . ' ' . $matches[2];
}
];
// 應用替換
$formatted = preg_replace_callback_array($patterns, $log);
// 輸出結果
echo nl2br(htmlspecialchars($formatted));
?>
運行上述腳本後,日誌內容將被格式化為:
127.0.0.1 - - 2025-04-21 15:32:01 "GET /index.php HTTP/1.1" 200
192.168.1.10 - - 2025-04-21 15:32:01 "POST /submit.php HTTP/1.1" 404
通過preg_replace_callback_array ,我們能夠用非常優雅的方式處理日誌中多個不同格式的字段。它的優勢在於一次性處理多個模式,每個模式獨立擁有自己的回調函數,邏輯清晰,維護方便。