需求分析
- 記錄使用者瀏覽過的商品/文章 ID
- 最新瀏覽優先顯示
- 去重:同一 ID 只保留最新時間
- 限制筆數(如 10 筆),超過移除最舊
- 資料持久化於 Cookie(客戶端),無需資料庫
完整實作
class BrowseHistory
{
const COOKIE_NAME = 'browse_history';
const MAX_ITEMS = 10;
const EXPIRE_DAYS = 30;
public static function add(int $itemId): bool
{
$history = self::get();
// 移除舊有相同項目
$history = array_values(array_filter($history, fn($item) => $item['id'] !== $itemId));
// 加入最新
array_unshift($history, [
'id' => $itemId,
'time' => date('c'), // ISO 8601 格式
]);
// 限制筆數
if (count($history) > self::MAX_ITEMS) {
$history = array_slice($history, 0, self::MAX_ITEMS);
}
return self::save($history);
}
public static function get(): array
{
if (empty($_COOKIE[self::COOKIE_NAME])) {
return [];
}
$data = json_decode($_COOKIE[self::COOKIE_NAME], true);
return is_array($data) ? $data : [];
}
private static function save(array $history): bool
{
$json = json_encode($history);
// Cookie 大小限制 4KB,若超過可改用 LocalStorage + AJAX 同步
return setcookie(
self::COOKIE_NAME,
$json,
time() + self::EXPIRE_DAYS * 86400,
'/', // 路徑
'', // 域名(空=當前)
false, // Secure (HTTPS 才傳)
true // HttpOnly (防 XSS 讀取)
);
}
public static function clear(): bool
{
return setcookie(self::COOKIE_NAME, '', time() - 3600, '/');
}
}
使用範例
// 商品頁面
require 'BrowseHistory.php';
$productId = (int)($_GET['id'] ?? 0);
if ($productId > 0) {
BrowseHistory::add($productId);
}
// 顯示歷史(側邊欄)
$history = BrowseHistory::get();
if ($history) {
echo '最近瀏覽
';
foreach ($history as $item) {
// 這裡需自行查詢商品標題/連結
echo "- 商品 #{$item['id']} ({$item['time']})
";
}
echo '
';
}
注意事項與替代方案
| 限制 | 說明 | 替代方案 |
| Cookie 大小 4KB | 約存 50-100 筆簡單記錄 | 改用 LocalStorage (5MB) + 定期同步至後端 |
| 每次請求自動送出 | 增加頻寬 | 關鍵路徑排除 Cookie 域名/路徑 |
| 用戶可清除/封鎖 | 資料不保證永久 | 登入用戶改存資料庫 (user_id, item_id, viewed_at) |
| 跨裝置不同步 | Cookie 綁定瀏覽器 | 登入後合併至雲端記錄 |
進階:加密 Cookie 內容
// 儲存前加密
$encrypted = openssl_encrypt(json_encode($history), 'aes-256-gcm', $key, OPENSSL_RAW_DATA, $iv, $tag);
$payload = base64_encode($iv . $tag . $encrypted);
setcookie(self::COOKIE_NAME, $payload, ...);
// 讀取時解密
$raw = base64_decode($_COOKIE[self::COOKIE_NAME]);
$iv = substr($raw, 0, 16);
$tag = substr($raw, 16, 16);
$ciphertext = substr($raw, 32);
$json = openssl_decrypt($ciphertext, 'aes-256-gcm', $key, OPENSSL_RAW_DATA, $iv, $tag);
$history = json_decode($json, true);
- 上一篇文章: PHP 分頁類別實作:靈活的頁碼導航產生器
- 下一篇文章: Symfony 十分鐘入門:從安裝到完成 CRUD 完整實戰
Copyright ©
程式碼之家 All Rights Reserved