中小型投票活動若不想架設資料庫,採用 PHP + 文字檔 是輕量級替代方案。本文提供完整原始碼,包含前端表單、後端邏輯、結果顯示與長條圖視覺化,並說明關鍵安全性考量。
票數1|票數2|...|IP1|IP2|...)<?php
header('Content-Type: text/html; charset=UTF-8');
$dataFile = 'data.txt'; // 選項檔
$voteFile = 'survey.txt'; // 統計檔
// 讀取選項
$options = file($dataFile, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
if (!$options) die('找不到投票選項檔案');
// 讀取統計資料
$voteData = file($voteFile, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
$statsLine = $voteData[0] ?? str_repeat('0|', count($options));
$votes = explode('|', trim($statsLine, '|'));
// 確保陣列長度一致
$votes = array_pad($votes, count($options), 0);
// 已投票 IP 記錄 (從第 2 行開始)
$votedIPs = array_slice($voteData, 1);
$clientIP = $_SERVER['REMOTE_ADDR'];
// 處理投票動作
if (isset($_POST['go']) && $_POST['go'] === '1') {
$choice = (int) ($_POST['vote'] ?? -1);
// 驗證選項有效性
if ($choice < 0 || $choice >= count($options)) {
die('無效的投票選項');
}
// 防重複投票 (簡易 IP 檢查)
if (in_array($clientIP, $votedIPs)) {
die("<center><font color='red'>您已投過票,感謝參與!</font></center>");
}
// 寫入統計
$votes[$choice]++;
$newStats = implode('|', $votes) . "\n" . implode("\n", array_merge([$clientIP], $votedIPs));
file_put_contents($voteFile, $newStats, LOCK_EX);
// 導向結果頁 (避免重複提交)
header('Location: survey.php?result=1');
exit;
}
// 顯示結果模式
if (isset($_GET['result'])) {
$totalVotes = array_sum($votes);
echo "<table cellpadding='10' border='1'>";
foreach ($options as $i => $option) {
$count = (int) $votes[$i];
$percent = $totalVotes > 0 ? ($count / $totalVotes * 100) : 0;
$barWidth = (int) ($percent * 3); // 放大倍率可調
echo "<tr>";
echo "<td><b>" . htmlspecialchars($option) . "</b></td>";
echo "<td><img src='smilies/bar.gif' height='20' width='{$barWidth}' align='middle'> ";
echo sprintf("%.1f%%", $percent) . "</td>";
echo "<td align='center'>{$count} 票</td>";
echo "</tr>";
}
echo "</table><p>總投票數:{$totalVotes}</p>";
exit;
}
// 顯示投票表單
?>
<!DOCTYPE html>
<html>
<head><meta charset="UTF-8"><title>線上投票</title></head>
<body>
<form method="post" action="survey.php">
<?php foreach ($options as $i => $opt): ?>
<p><input type="radio" name="vote" value="<?= $i ?>"> <?= htmlspecialchars($opt) ?></p>
<?php endforeach; ?>
<input type="hidden" name="go" value="1">
<p><input type="submit" value="送出投票"></p>
</form>
<p><a href="survey.php?result=1">查看結果</a></p>
</body>
</html>
最喜歡的程式語言 最常用的框架 偏好的資料庫 最愛的編輯器 常用的作業系統
12|8|15|20|5 192.168.1.100 192.168.1.101 ...
| 風險 | 現狀 | 改善建議 |
|---|---|---|
| 重複投票 | IP 檢查 (同 NAT 下用戶誤判) | 加入 Cookie/Session 二次驗證、要求登入 |
| 競態條件 | file_put_contents(..., LOCK_EX) | 高併發改用 SQLite/Redis 原子操作 |
| XSS 注入 | 輸出已用 htmlspecialchars() | 選項內容若含 HTML 需額外淨化 |
| 資料完整性 | 文字檔易損壞 | 定期備份、改用 JSON 結構化儲存 |
| 投票刷票 | 無防禦 | 驗證碼、頻率限制、用戶認證 |
checkbox,後端陣列處理expire=2024-12-31,投票前檢查export=csv 參數輸出 CSV文字檔投票系統部署簡單、零依賴,適合內部活動、小型問卷、教學示範。生產環境若需高併發、嚴格防刷、複雜權限,請遷移至資料庫方案 (MySQL/SQLite) 或第三方投票服務 (Google Forms, Typeform)。核心邏輯:讀取 → 驗證 → 原子寫入 → 視覺化,掌握此流程即可舉一反三。