萬盛學電腦網

您现在的位置: 萬盛學電腦網 >> 程式設計 >  >> 網路程式設計 >> PHP 程式設計

PHP Cookie 即時生效技巧:同步 $_COOKIE 與 setcookie 避免刷新頁面

問題根源

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'); // 即時可讀

進階:SameSite 與安全屬性

屬性推薦值說明
Securetrue僅 HTTPS 傳輸,生產環境必開
HttpOnlytrue禁止 JS 讀取,防 XSS
SameSiteLax / Strict防 CSRF,Lax 允許鏈接跳轉攜帶,Strict 完全禁止跨站
Path/整站可用,避免子目錄隔離
Domain.example.com共享給子域,前綴點號

常見誤區

  • ❌ setcookie('k','v'); echo $_COOKIE['k']; → 當前請求讀不到。
  • ❌ 以為 setcookie 立即生效 → 它只發送響應頭。
  • ❌ 刪除 Cookie 用 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() 解決下次請求攜帶**。封裝為工具類統一管理屬性,避免重複造輪子,提升安全性與維護性。

copyright © 萬盛學電腦網 all rights reserved