setcookie() 僅在 下一次 HTTP 響應頭 中發送 Set-Cookie,當前請求的 $_COOKIE 超全局變量 **不會自動更新**。導致「設置 Cookie 後立即讀取」取不到值,必須刷新頁面。
<?php
/**
* 設置 Cookie 並立即在當前請求生效
* @param string $name Cookie 名
* @param mixed $value 值(支援陣列)
* @param int $expire 過期時間戳,0=會話級
* @param string $path 路徑
* @param string $domain 域
* @param bool $secure 僅 HTTPS
* @param bool $httponly 防 XSS
*/
function set_cookie_now(string $name, mixed $value, int $expire = 0, string $path = '/', string $domain = '', bool $secure = false, bool $httponly = true): void {
// 1. 立即更新超全局變量(當前請求可讀)
if (is_array($value)) {
foreach ($value as $k => $v) {
$_COOKIE[$name][$k] = $v;
}
} else {
$_COOKIE[$name] = $value;
}
// 2. 發送 Set-Cookie 響應頭(下一次請求生效)
setcookie($name, $value, $expire, $path, $domain, $secure, $httponly);
}
// ===== 使用示例 =====
set_cookie_now('user_pref', ['theme' => 'dark', 'lang' => 'zh-TW'], time() + 86400);
// 當前請求即可讀取
echo $_COOKIE['user_pref']['theme']; // dark(無需刷新)
class CookieManager {
private array $defaults = [
'expire' => 0,
'path' => '/',
'domain' => '',
'secure' => false,
'httponly' => true,
'samesite' => 'Lax',
];
public function __construct(array $defaults = []) {
$this->defaults = array_merge($this->defaults, $defaults);
}
public function set(string $name, mixed $value, array $options = []): void {
$opts = array_merge($this->defaults, $options);
// 同步 $_COOKIE
if (is_array($value)) {
foreach ($value as $k => $v) {
$_COOKIE[$name][$k] = $v;
}
} else {
$_COOKIE[$name] = $value;
}
// 發送響應頭(PHP 7.3+ 支持 options 陣列)
if (PHP_VERSION_ID >= 70300) {
setcookie($name, $value, $opts);
} else {
setcookie($name, $value, $opts['expire'], $opts['path'], $opts['domain'], $opts['secure'], $opts['httponly']);
}
}
public function get(string $name, mixed $default = null): mixed {
return $_COOKIE[$name] ?? $default;
}
public function delete(string $name, array $options = []): void {
$opts = array_merge($this->defaults, $options, ['expire' => time() - 3600]);
unset($_COOKIE[$name]);
if (PHP_VERSION_ID >= 70300) {
setcookie($name, '', $opts);
} else {
setcookie($name, '', $opts['expire'], $opts['path'], $opts['domain'], $opts['secure'], $opts['httponly']);
}
}
public function has(string $name): bool {
return isset($_COOKIE[$name]);
}
}
// 使用
$cookie = new CookieManager(['domain' => '.example.com', 'secure' => true]);
$cookie->set('token', 'abc123', ['expire' => time() + 3600]);
echo $cookie->get('token'); // 即時可讀
| 屬性 | 推薦值 | 說明 |
|---|---|---|
| Secure | true | 僅 HTTPS 傳輸,生產環境必開 |
| HttpOnly | true | 禁止 JS 讀取,防 XSS |
| SameSite | Lax / Strict | 防 CSRF,Lax 允許鏈接跳轉攜帶,Strict 完全禁止跨站 |
| Path | / | 整站可用,避免子目錄隔離 |
| Domain | .example.com | 共享給子域,前綴點號 |
setcookie('k','v'); echo $_COOKIE['k']; → 當前請求讀不到。setcookie 立即生效 → 它只發送響應頭。setcookie('k','') 不帶過期時間 → 只是設置空值,未刪除。expire=time()-3600(過去時間)。domain=.example.com(注意前綴點)。<?php
// test_cookie.php
require 'CookieManager.php';
$cookie = new CookieManager();
if (isset($_GET['set'])) {
$cookie->set('test_key', 'test_value_' . time(), ['expire' => time() + 60]);
echo "已設置,立即讀取";
exit;
}
if (isset($_GET['read'])) {
echo "當前請求讀取: " . ($cookie->get('test_key') ?? '無');
echo "
下次請求自動攜帶 Cookie";
exit;
}
echo "設置 Cookie 並立即讀取";
「雙重寫入」是核心:$_COOKIE[$name] = $value 解決當前請求讀取,**setcookie() 解決下次請求攜帶**。封裝為工具類統一管理屬性,避免重複造輪子,提升安全性與維護性。