系統設計目標
- 無資料庫依賴,廣告代碼與權重儲存於文字檔(
ads/ads.txt)。
- 支援加權隨機:權重越高,顯示機率越大。
- 記錄每次展示的 IP、時間、廣告編號(
ads/ads.log),便於統計分析。
- 單一函式
myads() 即可在頁面任意位置呼叫。
廣告資料檔格式 ads/ads.txt
display=10
<a href="https://example.com"><img src="logo.gif" alt="廣告1"></a>
################################
display=20
<a href="https://example.com/page2">文字鏈接廣告</a>
################################
- 每組廣告以
################################ 分隔。
- 首行
display=權重(整數),權重總和作為隨機範圍上限。
- 後續行為廣告 HTML 代碼,直到下一個分隔線或檔案結尾。
核心函式 myads() 完整代碼
<?php
function myads() {
$dir = "ads";
$ads_file = "$dir/ads.txt";
$log_file = "$dir/ads.log";
$lines = file($ads_file, FILE_IGNORE_NEW_LINES);
$ads_count = 0;
$display_arr = [];
$total_weight = 0;
// 解析權重
foreach ($lines as $line) {
if (strncmp($line, 'display=', 8) === 0) {
$ads_count++;
$weight = (int)substr($line, 8);
$display_arr[$ads_count] = $weight;
$total_weight += $weight;
}
}
// 加權隨機選擇廣告編號
srand((double)microtime() * 1000000);
$rand = rand(1, $total_weight);
$acc = 0;
$selected = 1;
for ($i = 1; $i <= $ads_count; $i++) {
$acc += $display_arr[$i];
if ($rand <= $acc) { $selected = $i; break; }
}
// 輸出對應廣告 HTML
$num = 0;
$output = false;
foreach ($lines as $line) {
if (strncmp($line, 'display=', 8) === 0) { $num++; continue; }
if ($num === $selected) {
if ($line === '################################') break;
echo $line;
}
}
// 記錄展示日誌
$fp = fopen($log_file, 'a');
fwrite($fp, date("Y-m-d H:i:s ") . $_SERVER['REMOTE_ADDR'] => $selected . "\n");
fclose($fp);
}
?>
呼叫方式
<?php myads(); ?>
演算法解析:加權隨機選擇
- 累加所有廣告權重得
total_weight。
- 產生 1 ~ total_weight 之間的隨機數
$rand。
- 遍歷權重陣列累加,當累加值
$acc >= $rand 時,對應廣告被選中。
- 時間複雜度 O(n),n 為廣告數量,極輕量。
擴展與改進建議
| 需求 | 實現方向 |
| 點擊統計 | 廣告連結導向中轉腳本 click.php?id=N,記錄點擊後 302 跳轉真實 URL。 |
| 按時間/地區投放 | 在 ads.txt 增加 start_date, end_date, country 欄位,展示前過濾。 |
| 防刷/頻率限制 | 日誌分析同 IP 短時段重複展示,動態降權或暫停。 |
| 效能優化 | 將解析後的廣告陣列快取至 APCu/Redis/檔案快取,避免每次讀檔解析。 |
| 資料庫遷移 | 介面不變,底層改查資料表,支援後台管理介面增刪改查。 |
安全提醒
- 廣告代碼來自可信來源,若允許用戶提交廣告,必須嚴格過濾 XSS(僅允許安全標籤屬性)。
- 日誌檔權限設為 640,防止洩露訪客 IP。
- 目錄
ads/ 應設定 php_flag engine off 防止腳本執行。