
想在「下載型錄」「立即報名」這類連結旁邊顯示「已經有 123 人點過」,又不想為了一個數字建立資料庫,最簡單的方式是用 PHP 把次數存在一個 txt 檔裡。
先說清楚:如果你只是想「知道」哪些連結被點了幾次,不需要公開顯示在網頁上,用 Google Analytics 4(GA4)更準確、也更省事,做法在文章後面。需要把數字顯示給訪客看的時候,再用下面的程式。
舊版程式碼的問題
這篇文章舊版的程式碼,是點擊 ?click=yes 的連結時,讀取 txt 裡的數字、加 1 再寫回去。概念很簡單,但有幾個問題:
- 同時有人點擊會漏算:兩個人同時點擊,兩邊都讀到 100、都寫回 101,實際上少算了一次。需要檔案鎖定,讓寫入排隊進行。
- 用網址參數計數:任何人重新整理
?click=yes的網址就能一直灌次數;搜尋引擎爬蟲、通訊軟體產生連結預覽時,也可能去開這個網址,莫名其妙多算。會改變資料的動作應該用 POST,而不是一般的連結。 - PHP 8 會出現警告:沒有帶參數打開頁面時,
$_GET['click']不存在,PHP 8 會顯示「Undefined array key」的警告。 - 計數檔放在網站公開目錄:任何人都能直接打開 txt 檔,甚至被其他程式寫入。
新版寫法:三個檔案
counter-lib.php:讀取與增加次數的共用函式。count.php:接收點擊、把次數加 1(只接受 POST)。- 你的網頁:顯示次數,訪客點擊時通知
count.php。
計數檔建議放在網站公開目錄以外的資料夾。例如網站根目錄是 public_html,就在它的上一層建立 counter-data 資料夾,並確認 PHP 有權限寫入。下面的範例假設三個檔案都放在 public_html 裡。
第一個檔案:counter-lib.php
PHP
<?php
declare(strict_types=1);
// 允許計數的項目:只有這裡列出的名稱才會被計數
const COUNTER_IDS = ['catalog', 'price-list'];
// 計數檔存放的資料夾:放在網站公開目錄的外面
const COUNTER_DIR = __DIR__ . '/../counter-data';
function counter_file(string $id): string
{
if (!in_array($id, COUNTER_IDS, true)) {
throw new InvalidArgumentException('不允許的計數項目');
}
return COUNTER_DIR . '/' . $id . '.txt';
}
function counter_read(string $id): int
{
$file = counter_file($id);
if (!is_file($file)) {
return 0;
}
$fp = fopen($file, 'r');
if ($fp === false) {
return 0;
}
flock($fp, LOCK_SH); // 共用鎖定:等寫入完成才讀取
$count = (int) stream_get_contents($fp);
flock($fp, LOCK_UN);
fclose($fp);
return $count;
}
function counter_increment(string $id): int
{
$fp = fopen(counter_file($id), 'c+'); // 檔案不存在就建立,存在也不會清空
if ($fp === false) {
throw new RuntimeException('無法開啟計數檔');
}
try {
if (!flock($fp, LOCK_EX)) { // 獨佔鎖定:同時有人點擊時排隊處理
throw new RuntimeException('無法鎖定計數檔');
}
$count = (int) stream_get_contents($fp) + 1;
ftruncate($fp, 0);
rewind($fp);
fwrite($fp, (string) $count);
fflush($fp);
flock($fp, LOCK_UN);
return $count;
} finally {
fclose($fp);
}
}幾個重點:
COUNTER_IDS列出允許計數的項目。檔名只會從這份清單產生,訪客送來的名稱不在清單裡就直接拒絕,就不會有人用../之類的路徑去讀寫其他檔案。flock加上LOCK_EX是獨佔鎖定:同時有好幾個人點擊時,會一個一個輪流「讀取、加 1、寫回」,不會互相覆蓋。fopen的c+模式:檔案不存在就建立,存在時不會先清空內容。
第二個檔案:count.php
PHP
<?php
declare(strict_types=1);
require __DIR__ . '/counter-lib.php';
// 只接受 POST,一般開網址 (GET) 不會計數
if (($_SERVER['REQUEST_METHOD'] ?? '') !== 'POST') {
http_response_code(405);
header('Allow: POST');
exit;
}
// 驗證輸入:必須是字串,而且在允許的清單裡
$id = $_POST['id'] ?? '';
if (!is_string($id) || !in_array($id, COUNTER_IDS, true)) {
http_response_code(400);
exit;
}
try {
counter_increment($id);
http_response_code(204); // 成功,不需要回傳內容
} catch (Throwable $e) {
error_log('counter: ' . $e->getMessage());
http_response_code(500);
}第三個檔案:你的網頁
網頁要是 .php 檔,才能讀取並顯示目前的次數:
PHP
<?php
declare(strict_types=1);
require __DIR__ . '/counter-lib.php';
$catalogCount = counter_read('catalog');
?>
<!DOCTYPE html>
<html lang="zh-Hant-TW">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>點擊計數範例</title>
</head>
<body>
<p>
<a href="/files/catalog.pdf" data-counter="catalog">下載產品型錄</a>
(已下載 <span data-counter-value="catalog" data-count="<?= $catalogCount ?>"><?= number_format($catalogCount) ?></span> 次)
</p>
<script>
document.querySelectorAll('a[data-counter]').forEach((link) => {
link.addEventListener('click', () => {
const id = link.dataset.counter;
navigator.sendBeacon('/count.php', new URLSearchParams({ id: id }));
// 畫面上的數字先加 1 (實際次數以伺服器記錄為準)
const output = document.querySelector('[data-counter-value="' + id + '"]');
if (output) {
const next = Number(output.dataset.count) + 1;
output.dataset.count = String(next);
output.textContent = next.toLocaleString();
}
});
});
</script>
</body>
</html>- 連結本身是一般的連結,沒有 JavaScript 也能正常開啟。
navigator.sendBeacon會在背景用 POST 送出計數,就算連結馬上跳到別的頁面,這個請求也會送出,不會拖慢連結的開啟。- 搜尋引擎爬蟲通常不會觸發點擊事件,不會被算進去。
- 要增加其他計數項目,先加進
counter-lib.php的COUNTER_IDS,再在連結加上對應的data-counter。
這個寫法的限制
- 用滑鼠中鍵、或按右鍵選「在新分頁中開啟」時,不會觸發
click事件,這些點擊不會被計算。 - 任何人都能自己送 POST 到
count.php灌次數。這個數字適合當作參考,不適合當作正式統計或抽獎的依據。 - 少數主機的檔案系統(例如部分網路磁碟)上,
flock可能沒有作用。流量大、或計數項目很多的網站,建議改用資料庫,一行 SQL 就能安全地加 1(程式裡請用預處理語句帶入 id):
SQL
UPDATE click_counters SET clicks = clicks + 1 WHERE id = 'catalog';只想知道點擊數據?用 GA4 更好
如果目的只是想知道「型錄被下載了幾次」「外部連結被點了幾次」,GA4 比自己寫計數器更適合:
- 不用寫程式就有的資料:GA4 的「加強型評估」預設就會記錄外連點擊(連到其他網站的連結)與檔案下載(PDF、Word、Excel、壓縮檔等常見格式),在事件報表裡可以看到
click、file_download這兩個事件。 - 追蹤站內特定按鈕:可以用 Google 代碼管理工具(GTM)設定點擊觸發條件;網站已經安裝 GA4 的 gtag.js 的話,也可以在點擊時送出自訂事件:
JavaScript
gtag('event', 'catalog_click');- 資料比較乾淨:GA4 會自動排除已知的機器人流量,還能看到點擊的人從哪裡來、用什麼裝置,比單純的數字更有參考價值。
GA4 的限制是數字無法直接顯示在網頁上給訪客看。所以最常見的搭配是:對內分析用 GA4,要公開顯示數字時才用本篇的計數器。
延伸閱讀
哪些按鈕有人點、哪些內容沒人看,是調整網站內容最實在的依據。正威網路行銷是 25 年經驗的基隆網頁設計工作室,也提供 SEO 搜尋引擎優化與 Google 關鍵字廣告服務,歡迎來聊聊。






發表留言
還沒有人留言,歡迎第一個分享你的想法或問題。留言經審核後才會顯示,Email 不會公開。