Session 類?

Session 類允許你維護用戶的 “狀態” 并跟蹤他們在瀏覽你的網站時的活動。

CodeIgniter 有一些用于會話(session)儲存的驅動程序,你可以在目錄的最后部分中看到它們:

使用 Session 類?

初始化會話?

會話通常會在每次加載頁面時在全局范圍內運行,因此應該恰當地初始化 Session 類。

訪問并初始化會話:

$session = \Config\Services::session($config);

$config 參數是可選的,它是你的應用程序配置。如果未提供,服務將會使用你的默認配置。

初始化成功后,可以用以下方式使用 Session 庫對象:

$session

或者,你可以使用使用默認配置的 helper 方法,這個版本閱讀起來會更友好一些,但是不能配置任何配置選項

$session = session();

Session 是怎樣工作的??

加載頁面后,Session 類將檢查用戶的瀏覽器是否發送了有效的會話 cookie。如果會話 Cookie 不存在 (或者如果它不匹配一個存儲在服務器上的會話ID或已過期), 新會話將被創建和保存。

如果確實存在有效的會話,則其信息將被更新。對于每次更新,如果配置了會話 ID ,則可以對其進行重新生成。

對你來說很重要的一點是,一旦初始化,Session 類就會自動運行。你無需執行任何操作即可導致上述現象發生。如下所示,你可以使用會話數據,但是讀取,寫入和更新會話的過程是自動的。

注解

在 CLI 下,Session 庫將自動停止運行,因為它只是一個完全基于 HTTP 協議的概念。

關于異步的說明?

除非你要開發使用 AJAX 的網站,否則可以跳過本節。但是,如果你遇到了性能問題,那么本說明正是你所需要的。

早期版本的 CodeIgniter 中的會話未實現鎖定,這意味著使用同一會話的兩個 HTTP 請求可以完全同時運行。使用更合適的技術術語就是,請求是非阻塞的。

但是,會話上下文中的非阻塞請求也意味著不安全,因為在一個請求中對會話數據的修改(或會話 ID 再生)可能會干擾第二個并發請求的執行。這個細節是許多問題的根源,也是 CodeIgniter 4 擁有完全重寫的 Session 庫的主要原因。

我們為什么要告訴你這個?因為在嘗試找出性能問題的原因之后,你可能會得出結論,鎖定是問題所在,因此研究了如何刪除鎖定……

不要那樣做!刪除鎖定是 錯誤 的,它將給你帶來更多問題!

鎖定不是問題,而是解決方案。你的問題是,你已經打開了會話,但已經處理了該會話,因此不再需要它。因此,你需要的是在不再需要當前請求后關閉會話。

$session->destroy();

什么是會話數據??

會話數據是與特定會話 ID(cookie)關聯的數組。

如果你以前在 PHP 中使用過會話,則應該熟悉 PHP 的 $_SESSION 全局變量 (如果不熟悉,請閱讀該鏈接上的內容)。

CodeIgniter 使用與 PHP 提供的會話處理程序機制相同的方式來訪問其會話數據。使用會話數據就像操作(讀取,設置和刪除) $_SESSION 數組一樣簡單。

此外,CodeIgniter 還提供 2 種特殊類型的會話數據,下面將進一步說明:閃存數據(flashdata)和臨時數據(tempdata)。

檢索會話數據?

會話數組中的任何信息都可以通過 $_SESSION 全局變量獲得:

$_SESSION['item']

或通過常規訪問器方法:

$session->get('item');

或通過魔術方法,例如 getter :

$session->item

甚至可以通過會話輔助函數:

session('item');

item 就是你所要獲取的項目所對應的數組的鍵。例如,要將先前存儲的“名稱”項分配給 $name 變量,你可以這樣做:

$name = $_SESSION['name'];

// 或者:
$name = $session->name

// 或者:
$name = $session->get('name');

注解

對于 get() 方法,如果你要訪問的項目不存在,返回 NULL。

如果要檢索所有現有的用戶數據,則可以簡單地省略 item 鍵(獲取器僅適用于單個屬性值):

$_SESSION

// 或者:
$session->get();

添加會話數據?

假設某個特定用戶登錄到你的網站。身份驗證后,你可以將其用戶名和電子郵件地址添加到會話中,從而使你可以全局使用該數據,而不必在需要時運行數據庫查詢。

你可以把 $_SESSION 看作像其他變量一樣,將數據簡單地分配給數組。或作為 $session 的屬性。

以前的 userdata 方法已被廢棄,但是你可以將包含新會話數據的數組傳遞給該 set() 方法:

$session->set($array);

此處 $array 是一個包含新數據的關聯數組,這是一個例子:

$newdata = [
    'username'  => 'johndoe',
    'email'     => 'johndoe@some-site.com',
    'logged_in' => TRUE
];

$session->set($newdata);

如果要一次為一個會話數據只添加一個值,則 set() 還支持以下語法:

$session->set('some_name', 'some_value');

如果要驗證會話值是否存在,只需使用 isset() 以下命令進行檢查:

// 如果'some_name'項目不存在或為 NULL,則返回 FALSE,反之則返回 TRUE
isset($_SESSION['some_name'])

或者你可以調用 has()

$session->has('some_name');

向會話數據推送新值?

push 方法用于將新值推送到作為數組的會話值上。例如,如果“興趣愛好”鍵包含一個興趣愛好數組,則可以將新值添加到數組中,如下所示:

$session->push('hobbies', ['sport'=>'tennis']);

刪除會話數據?

與其他任何變量一樣, $_SESSION 使用 unset() 通過以下方式取消設置的值:

unset($_SESSION['some_name']);

// 或者同時取消設置多個值

unset(
    $_SESSION['some_name'],
    $_SESSION['another_name']
);

同樣,就像 set() 可以用來向會話添加信息一樣, remove() 也可以通過傳遞會話數據的鍵來刪除信息。例如,如果要從會話數據數組中刪除“some_name”:

$session->remove('some_name');

此方法還接受要取消設置的項目鍵數組:

$array_items = ['username', 'email'];
$session->remove($array_items);

閃存數據?

CodeIgniter 支持“flashdata”,這是僅對下一個請求可用的會話數據,然后將其自動清除。

這可能非常有用,特別是對于一次性的信息,錯誤或狀態消息(例如:“記錄 2 已刪除”)。

應當注意,flashdata 變量是常規會話變量,在 CodeIgniter 會話處理程序內部進行管理。

要將現有條目標記為“flashdata”:

$session->markAsFlashdata('item');

如果要將多個項目標記為 flashdata,只需將鍵作為數組傳遞:

$session->markAsFlashdata(['item', 'item2']);

要添加閃存數據:

$_SESSION['item'] = 'value';
$session->markAsFlashdata('item');

或者使用以下 setFlashdata() 方法:

$session->setFlashdata('item', 'value');

你還可以通過與 set() 相同的方式,將一個數組傳遞給 setFlashdata()

讀取 flashdata 變量與通過 $_SESSION 以下方式讀取常規會話數據相同:

$_SESSION['item']

重要

get() 當通過鍵檢索單個項時,該方法將返回 flashdata 項。但是,從會話中獲取所有用戶數據時,它不會返回 flashdata。

但是,如果你想確定自己正在讀取“flashdata”(而不是其他種類的數據),則也可以使用以下 getFlashdata() 方法:

$session->getFlashdata('item');

或者,要獲取包含所有 flashdata 的數組,只需省略 key 參數:

$session->getFlashdata();

注解

getFlashdata() 如果找不到該項目,則該方法返回 NULL。

如果發現需要通過其他請求保留 flashdata 變量,則可以使用 keepFlashdata() 方法來實現。你可以傳遞單個項或一組 flashdata 項來保留。

$session->keepFlashdata('item');
$session->keepFlashdata(['item1', 'item2', 'item3']);

臨時數據?

CodeIgniter 還支持“tempdata”這種具有特定到期時間的會話數據。該值過期或會話過期或被刪除后,該值將自動刪除。

與 flashdata 相似,tempdata 變量由 CodeIgniter 會話處理程序在內部進行管理。

要將現有項目標記為“tempdata”,只需將其密鑰和有效時間(以秒為單位)傳遞給該 mark_as_temp() 方法:

// 'item' will be erased after 300 seconds
$session->markAsTempdata('item', 300);

你可以通過兩種方式將多個項目標記為臨時數據,具體取決于你是否希望它們都具有相同的到期時間:

// “item”和“item2”都將在 300 秒后過期
$session->markAsTempdata(['item', 'item2'], 300);

// 'item'將在 300 秒后刪除,而'item2'將在 240 秒后刪除
$session->markAsTempdata([
    'item'  => 300,
    'item2' => 240
]);

添加臨時數據:

$_SESSION['item'] = 'value';
$session->markAsTempdata('item', 300); // Expire in 5 minutes

或者使用以下 setTempdata() 方法:

$session->setTempdata('item', 'value', 300);

你還可以將數組傳遞給 set_tempdata()

$tempdata = ['newuser' => TRUE, 'message' => 'Thanks for joining!'];
$session->setTempdata($tempdata, NULL, $expire);

注解

如果省略了到期時間或將其設置為 0,則將使用默認的生存時間值為 300 秒(或 5 分鐘)。

要讀取 tempdata 變量,同樣可以通過 $_SESSION 超全局數組訪問它 :

$_SESSION['item']

重要

get() 當通過鍵檢索單個項目時,該方法將返回 tempdata 項目。但是,從會話中獲取所有用戶數據時,它不會返回 tempdata。

或者,如果你想確保自己正在讀取“tempdata”(而不是其他種類的數據),則也可以使用以下 getTempdata() 方法:

$session->getTempdata('item');

當然,如果要檢索所有現有的臨時數據:

$session->getTempdata();

注解

getTempdata() 如果找不到該項目,則該方法返回 NULL。

如果你需要在一個臨時數據過期之前刪除它,你可以在 $_SESSION 數組里面做到

unset($_SESSION['item']);

但是,這不會刪除使該特定項成為 tempdata 的標記(它將在下一個 HTTP 請求中失效),因此,如果你打算在同一請求中重用同一鍵,則需要使用 removeTempdata()

$session->removeTempdata('item');

銷毀會話?

要清除當前會話(例如,在注銷過程中),你可以簡單地使用 PHP 的 session_destroy() 函數或庫的 destroy() 方法。兩者將以完全相同的方式工作:

session_destroy();

// 或者

$session->destroy();

注解

這必須是你在同一請求期間執行的與會話有關的最后一個操作。銷毀會話后,所有會話數據(包括 flashdata 和 tempdata)將被永久銷毀,并且在同一請求期間功能將無法使用。

你還可以 stop() 通過刪除舊的 session_id,銷毀所有數據并銷毀包含會話 ID 的 cookie,使用該方法完全終止會話:

$session->stop();

訪問會話元數據?

在以前的 CodeIgniter 版本中,默認情況下,會話數據數組包括 4 個項目:“session_id”,“ip_address”,“user_agent”,“last_activity”。

這是由于會話如何工作的細節所致,但現在在我們的新實現中不再需要。但是,你的應用程序可能會依賴這些值,因此下面是訪問它們的替代方法:

  • session_id: session_id()
  • ip_address: $_SERVER['REMOTE_ADDR']
  • user_agent: $_SERVER['HTTP_USER_AGENT'] (unused by sessions)
  • last_activity: Depends on the storage, no straightforward way. Sorry!

會話首選項?

通常,CodeIgniter 可以使所有工作立即可用。但是,會話是任何應用程序中非常敏感的組件,因此必須進行一些仔細的配置。請花點時間考慮所有選項及其效果。

你將在 app/Config/App.php 文件中找到以下與會話相關的首選項:

配置項 默認 選項 描述
sessionDriver CodeIgniterSessionHandlersFileHandler CodeIgniterSessionHandlersFileHandler CodeIgniterSessionHandlersDatabaseHandler CodeIgniterSessionHandlersMemcachedHandler CodeIgniterSessionHandlersRedisHandler CodeIgniterSessionHandlersArrayHandler 使用的會話驅動程序
sessionCookieName ci_session [A-Za-z_-] characters only 會話 cookie 的名字
sessionExpiration 7200 (2 hours) Time in seconds (integer) 您希望會話持續的秒數。如果您希望會話不過期(直到瀏覽器關閉),請將值設置為零:0
sessionSavePath NULL None 指定存儲位置,取決于所使用的驅動程序。
sessionMatchIP FALSE TRUE/FALSE (boolean) 讀取會話 cookie 時是否驗證用戶的 IP 地址。 請注意,某些 ISP 會動態更改 IP,因此,如果您希望會話不過期,可能會將其設置為 FALSE。
sessionTimeToUpdate 300 Time in seconds (integer) 此選項控制會話類重新生成自身并創建新的頻率。會話 ID。將其設置為 0 將禁用會話 ID 再生。
sessionRegenerateDestroy FALSE TRUE/FALSE (boolean) 自動重新生成時是否銷毀與舊會話 ID 相關聯的會話 ID。 設置為 FALSE 時,垃圾收集器稍后將刪除數據。

注解

作為最后的選擇,如果未配置上述任何項,則會話庫將嘗試獲取 PHP 的與會話相關的 INI 設置以及舊式 CI 設置,例如“sess_expire_on_close”。但是,你永遠不要依賴此行為,因為它可能導致意外的結果或將來被更改。請正確配置所有內容。

除了上述值之外,cookie 和本機驅動程序還應用了 IncomingRequestSecurity 類共享的以下配置值:

配置項 默認 描述
cookieDomain ‘’ 會話適用的域
cookiePath / 會話適用的路徑
cookieSecure FALSE 是否僅在加密(HTTPS)連接上創建會話 cookie

注解

“cookieHTTPOnly”設置對會話沒有影響。出于安全原因,始終啟用 HttpOnly 參數。此外,“cookiePrefix”設置被完全忽略。

Session 驅動程序?

如前所述,Session 庫帶有 4 個處理程序或存儲引擎,你可以使用它們:

  • CodeIgniterSessionHandlersFileHandler
  • CodeIgniterSessionHandlersDatabaseHandler
  • CodeIgniterSessionHandlersMemcachedHandler
  • CodeIgniterSessionHandlersRedisHandler
  • CodeIgniterSessionHandlersArrayHandler

默認情況下,在 FileHandler 初始化會話時將使用驅動程序,因為它是最安全的選擇,并且有望在任何地方都可以使用(實際上每個環境都有一個文件系統)。

但是,可以選擇通過 app/Config/App.php 文件中的 public $sessionDriver 行選擇任何其他驅動程序。請記住,每個駕駛員都有不同的警告,因此在做出選擇之前,一定要使自己熟悉(如下)。

注解

在測試期間使用 ArrayHandler 并將其存儲在 PHP 數組中,同時防止數據被持久保存。

FileHandler 驅動程序(默認)?

“FileHandler”驅動程序使用你的文件系統來存儲會話數據。

可以肯定地說,它的工作原理與 PHP 自己的默認會話實現完全相同,但是如果這對你來說是一個重要的細節,請記住,它實際上不是相同的代碼,并且有一些限制(和優點)。

更具體地說,它不支持 directory level and mode formats used in session.save_path ,并且為了安全起見,大多數選項都經過硬編碼。相反, public $sessionSavePath 僅支持絕對路徑。

你還應該知道的另一件事是,確保不要使用公共可讀或共享目錄來存儲會話文件。確保 只有你 有權查看所選 sessionSavePath 目錄的內容。否則,任何能夠做到這一點的人都可以竊取當前的任何會話(也稱為“會話固定”攻擊)。

在類似 UNIX 的操作系統上,這通常是通過使用 chmod 命令在該目錄上設置 0700 模式權限來實現的,該命令僅允許目錄所有者對目錄執行讀取和寫入操作。但是要小心,因為 運行 腳本的系統用戶通常不是你自己的,而是“www-data”之類的東西,因此僅設置這些權限可能會破壞你的應用程序。

Instead, you should do something like this, depending on your environment 取而代之的是,你應該根據自己的環境執行類似的操作

mkdir /<path to your application directory>/Writable/sessions/
chmod 0700 /<path to your application directory>/Writable/sessions/
chown www-data /<path to your application directory>/Writable/sessions/

Bonus Tip?

某些人可能會選擇其他會話驅動程序,因為文件存儲通常較慢。這只有一半是正確的。

一個非常基本的測試可能會讓你相信 SQL 數據庫更快,但是在 99%的情況下,只有當你只有幾個當前會話時,這才是正確的。隨著會話數的增加和服務器負載的增加(這很重要),文件系統將始終勝過幾乎所有的關系數據庫設置。

此外,如果只考慮性能,則可能需要研究使用 tmpfs ,(警告:外部資源),它可以使會話快速發展。

DatabaseHandler 驅動程序?

“DatabaseHandler”驅動程序使用關系數據庫(例如 MySQL 或 PostgreSQL)來存儲會話。這是許多用戶中的一個流行選擇,因為它使開發人員可以輕松訪問應用程序中的會話數據 - 它只是數據庫中的另一個表。

但是,必須滿足一些條件:

  • 你不能使用持久連接。
  • 你不能在啟用 cacheOn 設置的情況下使用連接。

為了使用“DatabaseHandler”會話驅動程序,你還必須創建我們已經提到的該表,然后將其設置為你的 $sessionSavePath 值。例如,如果你想使用“ci_sessions”作為表名,則可以這樣做:

public $sessionDriver   = 'CodeIgniter\Session\Handlers\DatabaseHandler';
public $sessionSavePath = 'ci_sessions';

然后,當然要創建數據庫表…

對于 MySQL:

CREATE TABLE IF NOT EXISTS `ci_sessions` (
    `id` varchar(128) NOT NULL,
    `ip_address` varchar(45) NOT NULL,
    `timestamp` int(10) unsigned DEFAULT 0 NOT NULL,
    `data` blob NOT NULL,
    KEY `ci_sessions_timestamp` (`timestamp`)
);

對于 PostgreSQL:

CREATE TABLE "ci_sessions" (
    "id" varchar(128) NOT NULL,
    "ip_address" varchar(45) NOT NULL,
    "timestamp" bigint DEFAULT 0 NOT NULL,
    "data" text DEFAULT '' NOT NULL
);

CREATE INDEX "ci_sessions_timestamp" ON "ci_sessions" ("timestamp");

你還需要 根據你的“sessionMatchIP”設置 添加主鍵。以下示例在 MySQL 和 PostgreSQL 上均可使用:

// 當 sessionMatchIP = TRUE 時
ALTER TABLE ci_sessions ADD PRIMARY KEY (id, ip_address);

// 當 sessionMatchIP = FALSE 時
ALTER TABLE ci_sessions ADD PRIMARY KEY (id);

// 刪除先前創建的主鍵(在更改設置時使用)
ALTER TABLE ci_sessions DROP PRIMARY KEY;

你可以通過在 applicationConfigApp.php 文件中添加新行并使用要使用的組名來選擇要使用的數據庫組 :

public $sessionDBGroup = 'groupName';

如果你不想手工完成所有這些操作,則可以使用 session:migrationcli 中的命令為你生成一個遷移文件:

> php spark session:migration
> php spark migrate

該命令在生成代碼時將考慮 sessionSavePathsessionMatchIP 設置。

重要

由于缺少其他平臺上的建議性鎖定機制,因此僅正式支持 MySQL 和 PostgreSQL 數據庫。使用不帶鎖的會話會導致各種問題,尤其是在大量使用 AJAX 的情況下,我們不支持這種情況。 session_write_close() 如果遇到性能問題,請在處理完會話數據后使用。

RedisHandler 驅動程序?

注解

由于 Redis 沒有公開鎖定機制,因此該驅動程序的鎖定由一個單獨的值模擬,該值最多可保留 300 秒。

Redis 是一種存儲引擎,由于其高性能而通常用于緩存并廣受歡迎,這可能也是你使用’RedisHandler’會話驅動程序的原因。

缺點是它不像關系數據庫那樣普遍存在,并且需要在系統上安裝 phpredis PHP 擴展,并且沒有與 PHP 捆綁在一起。很有可能,僅當你已經熟悉 Redis 并將其用于其他目的時,才使用 RedisHandler 驅動程序。

與“FileHandler”和“DatabaseHandler”驅動程序一樣,你還必須通過該 $sessionSavePath 設置配置會話的存儲位置 。此處的格式有些不同,同時又很復雜。最好用 phpredis 擴展的 README 文件來解釋,所以我們將簡單地鏈接到它:

https://github.com/phpredis/phpredis

警告

CodeIgniter 的會話庫不使用實際的’redis’ session.save_handler。 注意上面鏈接中的路徑格式。

但是,對于最常見的情況,一個簡單的 host:port 配對就足夠了:

public $sessionDiver    = 'CodeIgniter\Session\Handlers\RedisHandler';
public $sessionSavePath = 'tcp://localhost:6379';

MemcachedHandler 驅動程序?

注解

由于 Memcached 沒有公開鎖定機制,因此該驅動程序的鎖定由一個單獨的值模擬,該值最多保留 300 秒。

除了可能的可用性外,“Memcached ”驅動程序的所有屬性都與“RedisHandler”驅動程序非常相似,因為 PHP 的 Memcached 擴展是通過 PECL 分發的,并且某些 Linux 發行版使其可以作為易于安裝的軟件包使用。

除此之外,對于 Redis 并沒有任何故意的偏見,關于 Memcached 的說法沒有多大不同 - 它也是一種流行的產品,通常用于緩存并以其速度著稱。

但是,值得注意的是,Memcached 給出的唯一保證是將值 X 設置為在 Y 秒后過期將導致在 Y 秒過去之后將其刪除(但不一定要在該時間之前過期)。這種情況很少發生,但是應該考慮,因為這可能會導致會話丟失。

$sessionSavePath 格式相當這里簡單,僅僅是一對 host:port

public $sessionDriver   = 'CodeIgniter\Session\Handlers\MemcachedHandler';
public $sessionSavePath = 'localhost:11211';

Bonus Tip?

還支持使用可選的 weight 參數作為第三個冒號 ( :weight ) 值的多服務器配置,但是我們必須注意,我們尚未測試這是否可靠。

如果要嘗試使用此功能(后果自負),只需用逗號分隔多個服務器路徑:

// 相比于 192.0.2.1 權重為 1,本地主機將獲得更高的優先級(5)。
public $sessionSavePath = 'localhost:11211:5,192.0.2.1:11211:1';