PHP Cookie

Cookie 是儲存在使用者瀏覽器中的小型文字資料,用於在多個頁面請求之間保存資訊。由於 HTTP 協定本身是無狀態的,Cookie 提供了一種方式讓伺服器「記住」使用者,常用於記住登入狀態、儲存偏好設定、追蹤購物車內容等。

使用 setcookie() 函數可以設定 Cookie。Cookie 會隨著 HTTP 回應標頭傳送給瀏覽器,因此必須在任何 HTML 輸出之前呼叫。

<?php
// 基本設定
setcookie('username', 'Alice');

// 設定過期時間(1 小時後)
setcookie('username', 'Alice', time() + 3600);

// 完整參數
setcookie(
    'username',           // 名稱
    'Alice',              // 值
    time() + 86400,       // 過期時間(1 天後)
    '/',                  // 路徑
    'example.com',        // 網域
    true,                 // 只在 HTTPS 傳送
    true                  // HttpOnly(JavaScript 無法存取)
);
?>
setcookie() 必須在任何輸出之前呼叫。

PHP 7.3+ 選項陣列

PHP 7.3 引入了更清晰的選項陣列語法,可以用具名參數取代多個位置參數,讓程式碼更易讀:

<?php
setcookie('username', 'Alice', [
    'expires' => time() + 86400,
    'path' => '/',
    'domain' => 'example.com',
    'secure' => true,
    'httponly' => true,
    'samesite' => 'Strict'  // Strict, Lax, 或 None
]);
?>

瀏覽器會在後續請求中自動傳送 Cookie,PHP 會將收到的 Cookie 存入 $_COOKIE 超全域陣列中。注意:剛設定的 Cookie 要到下一次請求才能讀取,因為 Cookie 是隨回應傳送、隨請求接收的。

<?php
// 檢查 Cookie 是否存在
if (isset($_COOKIE['username'])) {
    echo "Hello, " . $_COOKIE['username'];
}

// 使用 null 合併運算子
$username = $_COOKIE['username'] ?? 'Guest';
?>

要刪除 Cookie,需要將其過期時間設為過去的時間點。這會讓瀏覽器認為 Cookie 已過期而將其移除:

<?php
// 將過期時間設為過去
setcookie('username', '', time() - 3600);

// 或設定空值
setcookie('username', '', [
    'expires' => time() - 3600,
    'path' => '/'
]);
?>

安全注意事項

<?php
// 1. 不要儲存敏感資料
// 錯誤
setcookie('password', 'secret123');

// 2. 使用 HttpOnly 防止 XSS
setcookie('session', $value, ['httponly' => true]);

// 3. 使用 Secure 只在 HTTPS 傳送
setcookie('session', $value, ['secure' => true]);

// 4. 使用 SameSite 防止 CSRF
setcookie('session', $value, ['samesite' => 'Strict']);

// 5. 驗證 Cookie 值
$userId = $_COOKIE['user_id'] ?? null;
if ($userId !== null) {
    $userId = filter_var($userId, FILTER_VALIDATE_INT);
    if ($userId === false) {
        // 無效的值
    }
}
?>

實際範例

記住我功能

<?php
// 登入時
if ($rememberMe) {
    $token = bin2hex(random_bytes(32));
    // 儲存 token 到資料庫
    saveRememberToken($userId, $token);
    
    setcookie('remember_token', $token, [
        'expires' => time() + 30 * 24 * 60 * 60,  // 30 天
        'path' => '/',
        'secure' => true,
        'httponly' => true,
        'samesite' => 'Lax'
    ]);
}

// 檢查自動登入
if (!isLoggedIn() && isset($_COOKIE['remember_token'])) {
    $user = findUserByRememberToken($_COOKIE['remember_token']);
    if ($user) {
        login($user);
    }
}
?>

偏好設定

<?php
// 儲存偏好
$preferences = json_encode(['theme' => 'dark', 'lang' => 'zh-TW']);
setcookie('preferences', $preferences, time() + 365 * 24 * 60 * 60);

// 讀取偏好
$prefs = json_decode($_COOKIE['preferences'] ?? '{}', true);
$theme = $prefs['theme'] ?? 'light';
?>