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 和本機驅動程序還應用了 IncomingRequest 和 Security 類共享的以下配置值:
配置項 | 默認 | 描述 |
---|---|---|
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/
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
該命令在生成代碼時將考慮 sessionSavePath 和 sessionMatchIP 設置。
重要
由于缺少其他平臺上的建議性鎖定機制,因此僅正式支持 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';