Cookie 是伺服器透過 HTTP 標頭 Set-Cookie 要求瀏覽器儲存的小型資料片段。瀏覽器後續請求同域名會自動帶入 Cookie 標頭。PHP 自動解析並填入 $_COOKIE 超全域陣列。
setcookie(
string $name, // Cookie 名稱
string $value = "", // 值 (自動 urlencode)
int $expires = 0, // 過期 Unix 時間戳 (0 = 瀏覽器關閉失效)
string $path = "", // 生效路徑 (預設為當前腳本目錄)
string $domain = "", // 生效域名 (預設為當前域名,需包含兩個點如 .example.com)
bool $secure = false, // 僅 HTTPS 傳輸
bool $httponly = false // 僅 HTTP 存取,JS document.cookie 不可讀 (防 XSS)
);
關鍵限制:setcookie() 必須在**任何輸出之前**呼叫 (含空白、BOM),否則觸發 Headers already sent 錯誤。
CREATE TABLE users (
FirstName VARCHAR(20),
LastName VARCHAR(40),
email VARCHAR(40) PRIMARY KEY,
count INT DEFAULT 1
);
<?php
// 1. 檢查 Cookie 是否存在
if (isset($_COOKIE['Example'])) {
// 2. 解析 Cookie 值 (格式: FirstName&LastName&email&count)
[$FirstName, $LastName, $email, $count] = explode('&', $_COOKIE['Example']);
$count++;
// 3. 更新 Cookie (延長 1 小時)
$newVal = implode('&', [$FirstName, $LastName, $email, $count]);
setcookie('Example', $newVal, time() + 3600);
// 4. 同步更新資料庫
$pdo = new PDO('mysql:host=localhost;dbname=users', 'user', 'pass');
$stmt = $pdo->prepare("UPDATE users SET count = ? WHERE FirstName = ? AND LastName = ? AND email = ?");
$stmt->execute([$count, $FirstName, $LastName, $email]);
// 5. 顯示歡迎頁面
echo "<h1>Hello $FirstName $LastName</h1>";
echo "<p>這是您的第 $count 次訪問</p>";
echo "<p>Email: $email</p>";
} else {
// 無 Cookie -> 引導註冊
echo '<a href="reg.php">請先註冊</a>';
}
?>
<form method="post" action="reg1.php">
名字: <input name="FirstName" required><br>
姓氏: <input name="LastName" required><br>
Email: <input type="email" name="email" required><br>
<button type="submit">註冊</button>
</form>
<?php
$FirstName = trim($_POST['FirstName'] ?? '');
$LastName = trim($_POST['LastName'] ?? '');
$email = trim($_POST['email'] ?? '');
if (!$FirstName || !$LastName || !$email) {
die('資料不完整,請返回');
}
$pdo = new PDO('mysql:host=localhost;dbname=users', 'user', 'pass');
// 檢查是否已註冊
$stmt = $pdo->prepare("SELECT count FROM users WHERE FirstName = ? AND LastName = ? AND email = ?");
$stmt->execute([$FirstName, $LastName, $email]);
$row = $stmt->fetch();
if ($row) {
// 舊用戶:計數+1,更新 Cookie
$count = $row['count'] + 1;
$pdo->prepare("UPDATE users SET count = ? WHERE email = ?")->execute([$count, $email]);
} else {
// 新用戶:寫入資料庫,計數=1
$count = 1;
$pdo->prepare("INSERT INTO users (FirstName, LastName, email, count) VALUES (?, ?, ?, ?)")
->execute([$FirstName, $LastName, $email, $count]);
}
// 設定 Cookie (有效期 1 小時)
$cookieVal = implode('&', [$FirstName, $LastName, $email, $count]);
setcookie('Example', $cookieVal, time() + 3600);
echo "註冊成功!<a href='index.php'>進入首頁</a>";
?>
| 風險 | 對策 |
|---|---|
| Cookie 竊取 (XSS) | httponly=true 防 JS 讀取、secure=true 僅 HTTPS 傳輸 |
| Cookie 偽造/竄改 | 敏感資料**不可直接存 Cookie**,改存 Session ID,資料放伺服器端 Session/資料庫 |
| CSRF 攻擊 | 表單加入 CSRF Token、SameSite=Strict/Lax (PHP 7.3+ setcookie 第 8 參數) |
| 資訊洩漏 | 範例中 Cookie 含明文 Email、姓名,**生產環境嚴禁** |
| 編碼問題 | 值含特殊字元需 urlencode() / urldecode() (PHP 自動處理) |
// 登入驗證成功後
session_regenerate_id(true); // 防 Session Fixation
$_SESSION['user_id'] = $userId;
$_SESSION['last_active'] = time();
// Cookie 僅存 Session ID (PHPSESSID),預設 httponly+secure
// 可自訂 Cookie 參數
session_set_cookie_params([
'lifetime' => 3600,
'path' => '/',
'domain' => '.example.com',
'secure' => true,
'httponly' => true,
'samesite' => 'Lax'
]);
session_start();
| 陣列 | 來源 | 用途 |
|---|---|---|
$_COOKIE | HTTP Request Cookie: 標頭 | 讀取瀏覽器送來的 Cookie |
$_SESSION | 伺服器端 Session 儲存 (檔案/資料庫/Redis) | 伺服器端狀態,安全、容量大 |
setcookie() | HTTP Response Set-Cookie: 標頭 | 寫入/更新/刪除 Cookie |
Cookie 是 HTTP 無狀態協議下維持狀態的基石。PHP 提供便利的 $_COOKIE 讀取與 setcookie() 寫入,**但切記:Cookie 完全由客戶端控制,不可信任**。敏感資料、權限判斷、業務關鍵邏輯必須放在伺服器端 Session 或資料庫,Cookie 僅作為識別鍵 (Session ID) 或非敏感偏好設定儲存。