PHP Conference Japan 2024

setcookie

(PHP 4, PHP 5, PHP 7, PHP 8)

setcookie傳送 Cookie

說明

setcookie(
    字串 $name,
    字串 $value = "",
    整數 $expires_or_options = 0,
    字串 $path = "",
    字串 $domain = "",
    布林值 $secure = false,
    布林值 $httponly = false
): 布林值

PHP 7.3.0 起提供替代簽章(不支援命名參數)

setcookie(字串 $name, 字串 $value = "", 陣列 $options = []): 布林值

setcookie() 定義一個與其他 HTTP 標頭一起傳送的 Cookie。如同其他標頭,Cookie 必須在您的腳本輸出任何內容「之前」傳送(這是協議限制)。這要求您在任何輸出之前呼叫此函式,包括 <html><head> 標籤以及任何空白字元。

設定 Cookie 後,可以在下一次載入頁面時使用 $_COOKIE 陣列存取它們。Cookie 值也可能存在於 $_REQUEST 中。

參數

» RFC 6265 提供了關於如何解讀每個 setcookie() 參數的規範參考。

name(名稱)

Cookie 的名稱。

value(值)

Cookie 的值。此值儲存在用戶端電腦上;請勿儲存敏感資訊。假設 name'cookiename',則此值可透過 $_COOKIE['cookiename'] 擷取。

expires_or_options(到期時間或選項)

Cookie 的到期時間。這是一個 Unix 時間戳記,以自 epoch 以來的秒數表示。一種設定方式是將 Cookie 到期前的秒數加到呼叫 time() 的結果。例如,time()+60*60*24*30 會將 Cookie 設定為 30 天後到期。另一種選擇是使用 mktime() 函式。如果設為 0 或省略,Cookie 將在工作階段結束時(瀏覽器關閉時)到期。

注意事項:

您可能會注意到 expires_or_options 參數採用 Unix 時間戳記,而不是日期格式 Wdy, DD-Mon-YYYY HH:MM:SS GMT,這是因為 PHP 在內部進行了此轉換。

path(路徑)

Cookie 在伺服器上可用的路徑。如果設為 '/',則 Cookie 在整個 domain 中都可用。如果設為 '/foo/',則 Cookie 僅在 domain/foo/ 目錄及其所有子目錄(例如 /foo/bar/)中可用。預設值是設定 Cookie 的目前目錄。

domain(網域)

Cookie 可用的(子)網域。將此設定為子網域(例如 'www.example.com')將使 Cookie 可用於該子網域及其所有其他子網域(例如 w2.www.example.com)。要使 Cookie 可用於整個網域(包括其所有子網域),只需將值設定為網域名稱(在本例中為 'example.com')。

仍在實作已棄用的 » RFC 2109 的舊版瀏覽器可能需要前導 . 才能匹配所有子網域。

secure(安全)

表示 Cookie 應該僅通過來自客戶端的安全 HTTPS 連線傳輸。當設定為 true 時,Cookie 將僅在存在安全連線時設定。在伺服器端,程式設計師有責任僅在安全連線上發送此類 Cookie(例如,參考 $_SERVER["HTTPS"])。

httponly

當設定為 true 時,Cookie 將僅能通過 HTTP 協定存取。這表示 Cookie 將無法通過腳本語言(例如 JavaScript)存取。有人認為此設定可以有效幫助減少通過 XSS 攻擊進行的身分竊取(儘管並非所有瀏覽器都支援),但這種說法經常受到爭議。truefalse

options

一個關聯式 陣列,可以包含 expirespathdomainsecurehttponlysamesite 這些鍵值。如果存在任何其他鍵值,則會產生等級為 E_WARNING 的錯誤。這些值與同名參數的描述具有相同的含義。samesite 元素的值應為 NoneLaxStrict。如果未提供任何允許的選項,則它們的預設值與明確參數的預設值相同。如果省略 samesite 元素,則不會設定 SameSite Cookie 屬性。

注意事項:

要設定包含未列在鍵值中的屬性的 Cookie,請使用 header() 函式。

傳回值

如果在呼叫此函式之前已存在輸出,setcookie() 將會失敗並傳回 false。如果 setcookie() 成功執行,它將傳回 true。這並不表示使用者接受了 Cookie。

更新日誌

版本 說明
8.2.0 Cookie 的日期格式現在是 'D, d M Y H:i:s \G\M\T';以前是 'D, d-M-Y H:i:s T'
7.3.0 已新增支援 options 陣列的替代簽章。此簽章也支援設定 SameSite Cookie 屬性。

範例

以下範例示範了一些發送 Cookie 的方法。

範例 #1 setcookie() 發送範例

<?php

$value
= 'something from somewhere';

setcookie("TestCookie", $value);
setcookie("TestCookie", $value, time()+3600); /* 一小時後過期 */
setcookie("TestCookie", $value, time()+3600, "/~rasmus/", "example.com", true);

?>

請注意,Cookie 的值的部分在您發送 Cookie 時會自動進行 URL 編碼,並且在接收時,它會自動解碼並賦值給與 Cookie 名稱相同的變數。如果您不想要這樣,您可以改用 setrawcookie() 函式。要在腳本中查看我們的測試 Cookie 的內容,只需使用以下範例之一

<?php
// 顯示單個 Cookie
echo $_COOKIE["TestCookie"];

// 另一種除錯/測試的方法是查看所有 Cookie
print_r($_COOKIE);
?>

範例 #2 setcookie() 刪除範例

刪除 Cookie 時,您應確保過期日期設定在過去,以觸發瀏覽器中的移除機制。以下範例說明如何刪除先前範例中發送的 Cookie

<?php
// 將過期日期設定為一小時前
setcookie("TestCookie", "", time() - 3600);
setcookie("TestCookie", "", time() - 3600, "/~rasmus/", "example.com", 1);
?>

範例 #3 setcookie() 和陣列

您也可以使用陣列表示法在 cookie 名稱中設定陣列 cookie。這會產生與您擁有的陣列元素數量一樣多的 cookie,但是當您的腳本收到 cookie 時,所有值都會被放置在與 cookie 名稱相同的陣列中。

<?php
// 設定 cookies
setcookie("cookie[three]", "cookiethree");
setcookie("cookie[two]", "cookietwo");
setcookie("cookie[one]", "cookieone");

// 頁面重新載入後,印出它們
if (isset($_COOKIE['cookie'])) {
foreach (
$_COOKIE['cookie'] as $name => $value) {
$name = htmlspecialchars($name);
$value = htmlspecialchars($value);
echo
"$name : $value <br />\n";
}
}
?>

上述範例將輸出

three : cookiethree
two : cookietwo
one : cookieone

注意在 cookie 名稱中使用分隔符號,例如 [],並不符合 RFC 6265 第 4 節的規範,但根據 RFC 6265 第 5 節,使用者代理程式應該支援此用法。

注意事項

注意事項:

您可以使用輸出緩衝在呼叫此函式之前傳送輸出,但所有輸出到瀏覽器的資料都會被緩衝在伺服器中,直到您傳送它為止。您可以透過在腳本中呼叫 ob_start()ob_end_flush(),或是在您的 php.ini 或伺服器設定檔中設定 output_buffering 設定指令來完成此操作。

常見陷阱

  • Cookie 在下次載入應顯示 cookie 的頁面之前,將不會顯示。要測試 cookie 是否已成功設定,請在 cookie 到期之前,在下一個載入的頁面上檢查 cookie。到期時間是透過 expires_or_options 參數設定的。一個簡單的除錯 cookie 存在性的方法是呼叫 print_r($_COOKIE);
  • 刪除 Cookie 時必須使用與設定時相同的參數。如果 value 參數是一個空字串,且所有其他參數都與先前呼叫 setcookie() 時的參數相符,則指定名稱的 cookie 將會從遠端用戶端刪除。這是在內部透過將值設定為 'deleted' 並將到期時間設定為過去來實現的。
  • 因為將 cookie 的值設定為 false 會嘗試刪除 cookie,所以您不應該使用布林值。請改用 *0* 代表 false,*1* 代表 true
  • Cookie 名稱可以設定為陣列名稱,並且在您的 PHP 腳本中可作為陣列使用,但個別的 cookie 會儲存在使用者的系統上。考慮使用 explode() 來設定一個包含多個名稱和值的 cookie。不建議使用 serialize() 來達到此目的,因為它可能導致安全漏洞。

多次呼叫 setcookie() 會按照呼叫的順序執行。

另請參閱

新增註釋

使用者貢獻的註釋 12 則註釋

389
walterquez
12 年前
除了這樣寫
<?php setcookie( "TestCookie", $value, time()+(60*60*24*30) ); ?>

你還可以這樣寫
<?php setcookie( "TestCookie", $value, strtotime( '+30 days' ) ); ?>
267
Bachsau
12 年前
想要移除 cookie 嗎?

很多人用複雜的方式
setcookie('name', 'content', time()-3600);

但為什麼要讓它這麼複雜,並冒著客戶端時間錯誤導致失效的風險呢?為什麼要繞著 time(); 打轉?

這是取消設定 cookie 最簡單的方法
setcookie('name', 'content', 1);

就這樣。
88
匿名
4 年前
只是一個例子來闡明陣列選項的用法,尤其因為 Mozilla 將棄用/懲罰使用 SameSite = none,如果沒有使用陣列選項,則預設使用此選項。

<?php
$arr_cookie_options
= array (
'expires' => time() + 60*60*24*30,
'path' => '/',
'domain' => '.example.com', // 為了相容性,前面加上點,或是使用子網域
'secure' => true, // 或 false
'httponly' => true, // 或 false
'samesite' => 'None' // None || Lax || Strict
);
setcookie('TestCookie', 'The Cookie Value', $arr_cookie_options);
?>
1
ilya at ilya dot top
3 個月前
在任何網頁瀏覽器中,都有一個非常常用的選項「開啟之前的視窗和分頁」,預設情況下是停用的,但許多人會啟用它。
當此選項處於啟用狀態時,網頁瀏覽器在關閉並重新開啟時,並不會執行終止並啟動新工作階段的動作,而是會儲存並還原目前的階段,連同工作階段 Cookie 和 sessionStorage。
與預期相反,工作階段 Cookie 和 sessionStorage 都可以存活很長時間,直到使用者在關閉網頁瀏覽器之前關閉分頁為止。
如果您希望 Cookie(例如具有時間偏移的 Cookie)保證具有較短的存活期,則應明確指定此較短的存活期,而不是依賴工作階段 Cookie 的自動刪除功能。
35
paul nospam AT nospam sitepoint dot com
17 年前
請注意,設定「陣列 Cookie」時,會為陣列的每個元素設定一個單獨的 Cookie。

在高流量的網站上,這會大幅增加客戶端後續 HTTP 請求的大小(包括對同一個網域上的靜態內容的請求)。

更重要的是,Cookie 規範規定瀏覽器每個網域只需接受 20 個 Cookie。Firefox 將此限制增加到 50 個,Opera 增加到 30 個,但 IE6 和 IE7 則強制執行每個網域 20 個 Cookie 的限制。超出此限制的任何 Cookie 都會取代較舊的 Cookie,或被瀏覽器忽略/拒絕。
20
nacho at casinelli dot com
7 年前
值得一提的是:您應該避免在 Cookie 名稱中使用點號。

<?php
// 這實際上會設定 'ace_fontSize' 名稱:
setcookie( 'ace.fontSize', 18 );
?>
40
匿名
17 年前
這裡沒有清楚說明的一點,讓我完全困惑了一陣子,那就是網域名稱必須至少包含兩個點號 (.),因此 'localhost' 是無效的,瀏覽器會拒絕設定 Cookie!對於 localhost,您應該使用 false。

要讓你的程式碼在本地端和正式網域都能運作,你可以這樣做:

<?php

$domain
= ($_SERVER['HTTP_HOST'] != 'localhost') ? $_SERVER['HTTP_HOST'] : false;
setcookie('cookiename', 'data', time()+60*60*24*365, '/', $domain, false);

?>
18
gabe at fijiwebdesign dot com
17 年前
如果你想刪除網域上的所有 Cookie,你可能會想使用以下的值:

<?php $_SERVER['HTTP_COOKIE'] ?>

而不是

<?php $_COOKIE ?>

來判斷 Cookie 名稱。
如果 Cookie 名稱是陣列表示法,例如:user[username]
那麼 PHP 會在 $_COOKIE 中自動建立一個對應的陣列。請改用 $_SERVER['HTTP_COOKIE'],因為它反映了實際的 HTTP 請求標頭。

<?php

// 刪除 cookies
if (isset($_SERVER['HTTP_COOKIE'])) {
$cookies = explode(';', $_SERVER['HTTP_COOKIE']);
foreach(
$cookies as $cookie) {
$parts = explode('=', $cookie);
$name = trim($parts[0]);
setcookie($name, '', time()-1000);
setcookie($name, '', time()-1000, '/');
}
}

?>
9
ellert at vankoperen dot nl
10 年前
注意事項:如果你使用 URL 重寫規則來將 domain.com/bla/stuf/etc 之類的東西轉換成參數,那麼在設定 Cookie 時可能會遇到問題。
至少在我的設定中,其中一個參數的更改導致 Cookie 不再「存在」。
解決方法很簡單:指定網域。'/' 通常就可以了。
9
synnus at gmail dot com
4 年前
「PHPSESSID」Cookie 即將被拒絕,因為它的「sameSite」屬性設為「none」或無效值,且沒有「secure」屬性。要了解更多關於「sameSite」屬性的資訊,請造訪 https://developer.mozilla.org/docs/Web/HTTP/Headers/Set-Cookie/SameSite.

<?php
ini_set
("session.cookie_secure", 1);
session_start();

我的 PHP 程式碼 ....

?>
4
laffen
15 年前
請注意,$_COOKIE 變數不會儲存多個同名的 Cookie。在相同主機但不同子網域的情況下,設定兩個同名的 Cookie 是合法的。
<?php
setcookie
("testcookie", "value1hostonly", time(), "/", ".example.com", 0, true);
setcookie("testcookie", "value2subdom", time(), "/", "subdom.example.com", 0, true);
?>
瀏覽器發出的下一個請求會在 $_SERVER['HTTP_COOKIE'] 變數中包含這兩個 Cookie,但在 $_COOKIE 變數中只會找到其中一個。對 subdom.example.com 的請求會包含兩個 Cookie,而對 example.com 或 www.example.com 的瀏覽器請求只會發送值為「value1hostonly」的 Cookie。

<?php
$kaker
= explode(";", $_SERVER['HTTP_COOKIE']);
foreach(
$kaker as $val){
$k = explode("=", $val);
echo
trim($k[0]) . " => " . $k[1];
}

// 輸出
testcookie => value1hostonly
testcookie
=> value2subdom

?>
3
匿名
14 年前
Cookie 名稱中的句點(例如 user.name)在 $_COOKIE 陣列中似乎會顯示為底線(例如 user_name)。這表示,例如,必須使用 $_COOKIE["user_name"] 來讀取使用 setcookie("user.name" ...) 設定的 Cookie,這已經相當令人困惑了。

此外,變數 $_COOKIE["user_name"] 會保留 setcookie("user.name" ...) 設定的值,而且無論呼叫 setcookie("user_name" ...) 多少次,都無法更改此值。這個問題可以透過清除「user.name」Cookie 來輕鬆解決,但由於 $_COOKIE 中只有「user_name」,因此可能需要一些時間才能意識到這一點。

希望這可以節省大家一些時間。
To Top