早期利用 AltaVista `link:` 語法查詢指向網站的連結數,作為「線上人數」估算。現已完全失效 (AltaVista 2013 關閉),僅供歷史參考。
<?php
// 歷史參考代碼 - 不再可用
$url = 'https://www.example.com';
$encoded = rawurlencode($url);
$apiUrl = "https://www.altavista.com/cgi-bin/query?kl=XX&pg=q&text=yes&q=link:$encoded&search=Search";
$lines = @file($apiUrl);
$totalLinks = 0;
foreach ($lines as $line) {
if (preg_match('/About (.*) pages found/', $line, $m)) {
$totalLinks = (int)$m[1];
break;
} elseif (strpos($line, 'no document matching') !== false) {
$totalLinks = 0;
break;
}
}
echo "$totalLinks 人連結到 $url";
?>
建立資料表記錄訪客 IP、最後活動時間、請求 URI,定期清理過期記錄。
CREATE TABLE `online_users` (
`id` INT UNSIGNED NOT NULL AUTO_INCREMENT,
`ip` VARCHAR(45) NOT NULL, -- 支援 IPv6
`last_active` DATETIME NOT NULL, -- 最後活動時間
`uri` VARCHAR(255) DEFAULT NULL, -- 當前頁面
`user_id` INT UNSIGNED DEFAULT NULL, -- 登入用戶 ID (可選)
`session_id` VARCHAR(128) DEFAULT NULL, -- Session ID (可選)
PRIMARY KEY (`id`),
UNIQUE KEY `uk_ip` (`ip`), -- 同 IP 視為同一訪客
KEY `idx_last_active` (`last_active`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
<?php
// 設定
$duration = 180; // 線上判定秒數 (3 分鐘)
$pdo = new PDO('mysql:host=localhost;dbname=stats;charset=utf8mb4', 'user', 'pass', [
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC
]);
$ip = $_SERVER['REMOTE_ADDR'] ?? 'unknown';
$uri = $_SERVER['REQUEST_URI'] ?? '/';
$now = date('Y-m-d H:i:s');
$expireTime = date('Y-m-d H:i:s', time() - $duration);
// 1. 清理過期記錄 (機率性執行,減少寫入壓力)
if (mt_rand(1, 100) <= 10) { // 10% 機率
$pdo->exec("DELETE FROM online_users WHERE last_active < '$expireTime'");
}
// 2. 更新或插入當前訪客
$stmt = $pdo->prepare("
INSERT INTO online_users (ip, last_active, uri)
VALUES (?, ?, ?)
ON DUPLICATE KEY UPDATE last_active = VALUES(last_active), uri = VALUES(uri)
");
$stmt->execute([$ip, $now, $uri]);
// 3. 統計線上人數
$stmt = $pdo->query("SELECT COUNT(*) FROM online_users WHERE last_active >= '$expireTime'");
$onlineCount = (int)$stmt->fetchColumn();
echo "當前線上人數: $onlineCount";
?>
<?php
class OnlineCounter {
private $pdo;
private $redis;
private $duration;
private $table = 'online_users';
public function __construct(PDO $pdo, $duration = 180, Redis $redis = null) {
$this->pdo = $pdo;
$this->duration = $duration;
$this->redis = $redis;
}
public function hit(array $info = []): int {
$ip = $info['ip'] ?? ($_SERVER['REMOTE_ADDR'] ?? 'cli');
$uri = $info['uri'] ?? ($_SERVER['REQUEST_URI'] ?? '/');
$userId = $info['user_id'] ?? null;
$sessionId = $info['session_id'] ?? (session_id() ?: null);
$key = "online:$ip";
// Redis 快速計數 (排行榜/即時顯示)
if ($this->redis) {
$this->redis->hSet($key, [
'ip' => $ip,
'uri' => $uri,
'user_id' => $userId ?? '',
'session_id' => $sessionId ?? '',
'updated' => time()
]);
$this->redis->expire($key, $this->duration + 60);
// 定期同步至 MySQL (排程器執行)
return $this->getCountFromRedis();
}
// MySQL 直接模式
$expire = date('Y-m-d H:i:s', time() - $this->duration);
$now = date('Y-m-d H:i:s');
$sql = "INSERT INTO {$this->table} (ip, last_active, uri, user_id, session_id)
VALUES (?, ?, ?, ?, ?)
ON DUPLICATE KEY UPDATE last_active=?, uri=?, user_id=?, session_id=?";
$stmt = $this->pdo->prepare($sql);
$stmt->execute([$ip, $now, $uri, $userId, $sessionId, $now, $uri, $userId, $sessionId]);
// 機率性清理
if (mt_rand(1, 100) <= 5) {
$this->pdo->exec("DELETE FROM {$this->table} WHERE last_active < '$expire'");
}
return $this->getCount();
}
public function getCount(): int {
$expire = date('Y-m-d H:i:s', time() - $this->duration);
$stmt = $this->pdo->prepare("SELECT COUNT(*) FROM {$this->table} WHERE last_active >= ?");
$stmt->execute([$expire]);
return (int)$stmt->fetchColumn();
}
public function getCountFromRedis(): int {
// 掃描所有 online:* keys (生產環境建議用 SCAN 避免阻塞)
$keys = $this->redis->keys('online:*');
$count = 0;
$now = time();
foreach ($keys as $key) {
$data = $this->redis->hGetAll($key);
if (isset($data['updated']) && ($now - (int)$data['updated']) < $this->duration) {
$count++;
}
}
return $count;
}
public function getList(int $limit = 50): array {
// 取得線上用戶列表 (含頁面、用戶資訊)
$expire = date('Y-m-d H:i:s', time() - $this->duration);
$stmt = $this->pdo->prepare("
SELECT ip, uri, user_id, last_active
FROM {$this->table}
WHERE last_active >= ?
ORDER BY last_active DESC
LIMIT ?
");
$stmt->execute([$expire, $limit]);
return $stmt->fetchAll();
}
}
?>
| 維度 | AltaVista 反向連結 | MySQL Session 表 | Redis + MySQL 混合 |
|---|---|---|---|
| 可用性 | ❌ 已失效 | ✅ 成熟穩定 | ✅ 高效能 |
| 即時性 | 小時級延遲 | 秒級 | 毫秒級 |
| 準確度 | 極低 (連結≠人) | 高 (IP+時間) | 高 |
| 擴展性 | 無 | 中 (寫入瓶頸) | 極高 |
| 功能豐富度 | 無 | 基本統計 | 列表、排行、用戶關聯 |