網頁狀態管理 ― Cookie 與 Session 第 6 章 網頁狀態管理 ― Cookie 與 Session
本章重點 6 - 1 HTTP 通訊協定的 Stateless 特性 6 - 2 使用 Cookie 將狀態儲存在用戶端 6 - 3 使用 Session 於連線時保存狀態 6 - 4 各資料儲存方式的比較 6 - 5 實戰演練 - 上線次數計數器 6 - 6 實戰演練 - 機智問答過五關
6-1 HTTP 通訊協定的 Stateless 特性 在 HTTP 通訊協定中, 並沒有設計保存連線狀態的能力 (稱為 Stateless Connection)。 雖然我們在瀏覽器連線網站時, 有『上一頁』、『下一頁』、『回傳』等動作, 但是對伺服器而言, 每次送出網頁都是重新建立連線 (不論是否同一頁)。 所以每個 HTTP 連線之間都是獨立互不相干, 使用者輸入的資料, 或是網頁程式所產生的變數值, 都無法被保留下來。
HTTP 通訊協定的 Stateless 特性 以常見的購物網站為例, 如果不能記錄狀態, 那就好比我們放了一樣東西到購物車, 但是點選其他商品網頁之後, 它就『忘』了我們剛剛買了什麼。 可以想見, Stateless 特性對於網頁程式的設計來說非常地不便, 所以發展出許多技術將狀態保留下來, 以供後續連線繼續使用。 以下開始介紹如何使用 Cookie、Session 保存狀態。
6-2 使用 Cookie 將狀態儲存在用戶端 Cookie 的運作原理 建立、修改 Cookie 讀取與刪除 Cookie 存取範圍
Cookie 的運作原理 Cookie 是一種將狀態記錄於用戶端的技術, 下面是其運作的示意圖:
Cookie 的運作原理 當程式請用戶端儲存 Cookie 時, 只要瀏覽器支援 Cookie, 而且使用者未關閉此功能, 瀏覽器就會依照程式的要求, 將變數與變數值儲存於用戶端電腦內 (格式為純文字檔)。 使用者對伺服器的後續連線便會持續地帶有 Cookie, 讓程式可以取回先前保存的值:
Cookie 的運作原理
Cookie 的運作原理 因為 Cookie 儲存在用戶端電腦上, 只要使用者不將之刪除, 資料即可保留一段時間, 不會因為關閉瀏覽器而消失, 而且同一網站的各網頁之間也可以共享資料。 使用 Cookie 最常見的例子便是購物網站, 許多購物網站製作購物車時會使用 Cookie, 當使用者將產品放入購物車之後, 程式就會將資料寫入 Cookie。
Cookie 的運作原理 如此就算關閉瀏覽器或是電腦, 未來使用者只要以同一電腦環境連上購物網站, 程式便可由 Cookie 取回前次選取的商品, 將購物車恢復為上次的狀態。 使用 Cookie 時有以下的限制: 大多數瀏覽器會限制 Cookie 最大不得超過 4096 Bytes:所以 Cookie 只適合用來存放少量資料, 如使用者名稱、帳號、上次到訪時間...等。
Cookie 的運作原理 有些瀏覽器只允許每個網站儲存 20 個 Cookie, 若超過就會蓋掉舊的 Cookie, 不過目前有些瀏覽器已經放寬此限制。 因為 Cookie 是以純文字的方式儲存與傳送, 所以可能會被檢視、攔截或是竄改, 故重要或機密資料 (例如使用者密碼) 請勿存放於 Cookie。
建立、修改 Cookie 要建立 Cookie 必須使用 setcookie() 函式, 其基本語法如下: name 與 value 是必要的參數, 而 expire 參數可以視需要使用。 expire 是用來設定到期時間的參數, 設定該 Cookie 何時到期, 到期之後該 Cookie 會被刪除而消失。
建立、修改 Cookie 如果沒有設定到期時間, 則 Cookie 會在瀏覽器關閉後立刻刪除。 下面是 setcookie() 的使用範例: setcookie() 除了用來建立 Cookie 外, 亦可修改原有 Cookie 的值。
建立、修改 Cookie 此外, 由於 Cookie 資訊會放在 HTTP 通訊表頭中, 而 HTTP 的通訊表頭必須在所有輸出之前送出。 所以 setcookie() 函式執行之前, 若有 echo() 或 HTML 標籤等輸出, 將會導致 setcookie() 函式執行錯誤。
用 mktime() 指定特定的到期時間 指定到期時間時, 也可以使用 mktime() 函式指定特定的時間, 例如要指定 Cookie 在 2011 年 10 月 5 日 1 點 2 分 3 秒到期, 可使用下面方式: 前面使用 time() 設定的到期時間是『距離目前的 x 秒』, 例如『time() + 20 * 60』是目前時間的 20 分鐘後, 如果使用者於第 19 分鐘再次連線時, 到期時間會重新被設定為『第 19 分鐘的 20 分鐘後』。
用 mktime() 指定特定的到期時間 若是使用 mktime() 來設定到期時間, 則會明確地指定日期時間, 不會因為再次連線而延長到期時間。 所以 time() 與 mktime() 各有不同的適用環境, 如果希望 Cookie 隨著使用者重新連線而繼續保留, 便可使用 time() 函式, 若想要明確指定特定的到期時間, 則應該使用 mktime()。
讀取與刪除 Cookie 程式要讀取 Cookie 時, 只要使用內建變數 $_COOKIE[] 即可: 若要刪除 Cookie, 請使用 setcookie() 函式將到期時間設定為過去的時間, 表示該 Cookie 已經過期即可。例如: 下面範例將使用 Cookie 建立一個計數器, 記錄使用者到訪網站的次數。
讀取與刪除 Cookie
讀取與刪除 Cookie
讀取與刪除 Cookie 執行結果: 關閉瀏覽器後, 再重新開啟瀏覽器連線此程式, 仍然能正常顯示上站次數:
使用緩衝式輸出, 避免 setcookie() 的執行錯誤 前面提到 setcookie() 函式執行之前, 不能有 echo 指令或 HTML 標籤等輸出, 否則會導致 setcookie() 執行錯誤。 不過如果程式一定得在 setcookie() 之前執行 echo 或放置 HTML 標籤, 為了避免執行錯誤, 可以用 ob_start() 函式將輸出暫存在伺服器的緩衝區。 直到程式執行 setcookie() 之後, 再呼叫 ob_end_flush() 送出緩衝區暫存的資料 (或待程式結束後, PHP 也會自動輸出緩衝區內容)。
使用緩衝式輸出, 避免 setcookie() 的執行錯誤 以下是一個使用 ob_start() 的基本程式架構與示意圖:
使用緩衝式輸出, 避免 setcookie() 的執行錯誤 有關 ob_start() 的詳細使用方法請參考旗標公司出版的『PHP 函式庫參考手冊』, 或是 http://www.php.net/ob_start 網頁。
Cookie 陣列 當好幾個 Cookie 可以歸屬同一類時, 可以用 Cookie 陣列來管理。建立與修改 Cookie 陣列的語法如下:
Cookie 陣列 Cookie 陣列的讀取也是使用內建變數 $_COOKIE[], 不過因為 $_COOKIE[] 本身就是一個陣列, 所以讀取時會變成二維陣列的形式:
存取範圍 建立 Cookie 後, 在預設情況下, 只有同一個網站、同一個目錄、與子目錄下的網頁, 可以存取該 Cookie。
存取範圍 我們將 Ch06-01.php 複製成同一個目錄下的 Ch06-02.php, 同時開啟 2 個瀏覽器視窗, 並且分別瀏覽這兩個網頁。 您可以發現不管瀏覽那一個網頁, 上站次數都按順序遞增, 證明存取的是同一組 Cookie。
存取範圍 如果要讓不同網站或不同目錄的網頁使用同一個 Cookie 時, 就必須使用 setcookie() 函式設定存取路徑或存取網域, 其語法如下: path 與 domain 參數並非必要參數, 可以視需要使用, 以下分別介紹這 2 個參數。 存取路徑 存取路徑參數可以指定網站中那些路徑下的網頁可存取該 Cookie。
存取範圍 存取網域 路徑必須是網站的絕對路徑, 以下是設定存取路徑的範例: 當網頁程式要求讀取 Cookie 時, 瀏覽器會拿該 Cookie 的存取網域參數和 URL 中的網域比較, 如果相同, 才會將該 Cookie 傳送給程式使用。
存取範圍 所以當我們連線 http://www.flag.com.tw 時建立的 Cookie, 便無法在 http://sales.flag.com.tw 存取, 如果希望所有 *.flag.com.tw 網頁都可以存取 Cookie, 可以如下設定:
6-3 使用 Session 於連線時保存狀態 Session 的意義 Session 的運作原理 存取 Session
Session 的意義 一個 Session 代表使用者開啟瀏覽器連線網站, 一直到關閉瀏覽器為止的工作期間。
Session 的意義
Session 的意義 當網頁程式使用 Session 技術儲存狀態或資料時, 不論是否為相同頁面, 只要在同一個 Session 內即可存取同一份資料。 但若是不同 Session, 就算在同一部電腦、使用相同瀏覽器、連線相同網頁、讀寫同一名稱的資料, 其所存取的仍是兩份獨立不同狀態的資料。
Session 的運作原理 Session 是一種將資料記錄於伺服器的技術, 下面是其運作的示意圖: 由上面示意圖可以看到程式必須以 Session ID 來存取狀態與資料。
Session 的運作原理 一般情況下, Session ID 會存放在沒有到期日的 Cookie 內 (但是其他 Session 資料皆儲存於伺服器上)。 所以同一個網站的相同與不同網頁之間可以藉由 Cookie 掌握同一個 Session ID, 進而存取同一份資料。 不過因為 Cookie 沒有到期日, 代表其在關閉瀏覽器時會自動刪除, 因此關閉瀏覽器後重新連線同一網站, 得到的便是新的 Session ID。
Session 的運作原理 雖然 Session 必須藉由 Session ID 來存取資料, 不過 PHP 會自動處理 Session ID 的取得、建立、傳送等工作。 所以同一網站中各個程式都可以直接存取 Session 資料, 不需要特別處理 Session ID 的相關事宜。 Session 內的資料預設會存放於伺服器, 與 Cookie 不同, 所以較為安全, 不易被檢視或竄改, 因此可以用來儲存重要的狀態或資料。
Session 的運作原理 此外, 因為 Session 是隨著瀏覽器的開啟與關閉而生滅的機制, 而且將資料記錄在伺服器而非用戶端。 所以相當適合使用在會員登入、購物結帳...等, 各網頁需要共享機密或重要資料, 且每次連線都會因狀況不同而必須儲存不同資料的場合。
存取 Session 要在 PHP 程式中使用 Session, 只要先執行 session_start() 函式, 開啟 Session 功能, 即可在後續步驟存取 Session。 不過如前面所提到的, Session 預設使用 Cookie 傳遞 Session ID。 而 Cookie 資訊會放在 HTTP 通訊表頭中, 且 HTTP 的通訊表頭必須在所有輸出之前送出。
存取 Session 所以 session_start() 函式執行之前, 若有 echo()、或 HTML 標籤等輸出, 將會導致 session_start() 函式執行錯誤, 而無法使用 Session 功能。 執行 session_start() 後, 便可以用內建變數 $_SESSION[] 建立、修改 Session 變數。其語法如下:
存取 Session 建立、修改 Session 的範例如下: 設定 Session 變數之後, 只要使用 $_SESSION[name] 便可以讀取先前所存的資料:
刪除 Session 關閉瀏覽器時, 便無法存取之前的 Session。但是, 很多時候, 即使瀏覽器沒有關閉, 程式也有必要主動刪除 Session。 舉例來說, 網路銀行一定會提供使用者登出的按鈕, 讓使用者安心的離開。 這時, 程式就必須在使用者登出時刪除 Session。否則, 只要用瀏覽器的上一頁鈕回到之前的網頁, 就可以使用原有的 Session。
刪除 Session 要刪除 Session, 請用下面兩個函式: 下面是刪除 Session 的範例: 刪除所有 Session 變數:使用 session_unset(); 刪除一個 session 變數:使用 unset($_SESSION[name]); 下面是刪除 Session 的範例:
同一網頁存取 Session 前面程式 Ch06-01.php 使用 Cookie 記錄上站次數, 不過該程式有一個缺點, 只要使用者一直重新整理網頁, 上站次數就會一直增加。 這樣其實不太合理, 一般會將『關閉瀏覽器後, 下次再進入網站』才算增加一次上站次數。 因此我們可以使用 Session 的特點 (只要不關閉瀏覽器, 都算是同一個 Session), 修正該程式, 顯示比較正確的上站次數。 下面是改良後的程式碼。
同一網頁存取 Session
同一網頁存取 Session
同一網頁存取 Session 執行結果:
同一網站的不同網頁間存取 Session 上面的範例說明了如何讀取程式本身所建立的 Session, 接著將示範同一網站的不同網頁間如何存取 Session 資料。 我們將以程式顯示使用者逗留網站的時間, 下面是程式的運作的示意題:
同一網站的不同網頁間存取 Session 下面是 Ch06-04.php 與 Ch06-05.php 的程式碼:
同一網站的不同網頁間存取 Session
同一網站的不同網頁間存取 Session
同一網站的不同網頁間存取 Session 執行結果如下頁所示。
同一網站的不同網頁間存取 Session
Session 的有效時間 前面提到一個 Session 代表瀏覽器開啟到關閉為止的工作期間, 所以理論上只要不關閉瀏覽器, 就可以一直保有同一份 Session 資料。 不過實際上在 PHP 中 Session 的有效時間並沒有那麼長。 根據 php.ini 檔內的設定, 預設會自動刪除超過 24 分鐘沒有存取過的 Session 資料, 所以如果使用者的瀏覽器長達 24 分鐘沒有動作, Session 資料就會被刪除而遺失。
Session 的有效時間 有些狀況下, 可能需要更改 Session 的有效時間:例如論壇網站中使用者張貼文章的時間可能超過 24 分鐘, 此時便需要延長有效時間。 而網路銀行為了避免使用者暫時離開電腦時, 之前登入帳號可能被其他人所使用, 所以大都會將有效時間縮短。 如果您想要修改 Session 的有效時間, 請開啟 php.ini 設定檔, 如下修改設定:
不修改 php.ini, 變更 Session 有效時間的方法 http://blog.serv.idv.tw/2005/05/05/233/ (中文資料) http://www.captain.at/howto-php-sessions.php (英文資料)
6 - 4 各資料儲存方式的比較 本章介紹使用 Cookie 與 Session 來保留狀態資料, 加上之前就知道的變數 (常數), 以及後面章節會介紹的檔案與資料庫, 可能會讓人弄不清什麼時候要用哪個方法比較好。 下表將比較這些方法, 讓您瞭解各方法適合的使用時機。
各資料儲存方式的比較