Session 类別

Session 类別能让你为你的应用程序在無狀態(stateless)的 web 环境下保持狀態。 它能让你在伺服器上使用各種變数解決方案來儲存變数,并且在下个頁面請求再呼叫變数。

手动載入

在大多数情況下,配置所需的驅动是足夠的,使用方法記錄在 用法 頁面。 不過,也有一些情況你会想要更進一步控制 session 的行为方式。 你可能想要使用一个配置在 session 配置档案中不同的 session 驅动。 或你有多个驅动并行的需要。这就是为什么要有 forge 方法。

forge 方法回傳一个 session 类別的實例,使用定義在配置档案的驅动或透過參数。 你可以在回傳的物件使用紀錄在 用法 頁面的方法,使用动態呼叫。

forge($config = array())

forge 能让你手动實例化一个 session 驅动。

靜態
參数
參数 預設 描述
$config 選擇性 你可以透過简单地傳遞驅动名稱到 forge 方法來選擇驅动。 如果你需要更多的自訂配置,定義一个 $config 陣列,使用記錄在 配置 頁面上的參数。 这裡你傳遞的配置設定将覆寫在配置文件中定義的。
回傳 物件 - 實例化的 session 物件。
範例
// 實例化一个資料庫 session
$session = Session::forge('db');

// 取得來自 session 的 counter
$counter = $session->get('counter');

// 如果不存在,設定一个預設值
if ($counter === false)
{
	$counter = 0;
}

// 寫回 counter
$session->set('counter', $counter);

// 沒有需要明確寫入,Fuel 会打理好……

// 你也可以帶設定載入一个驅动,它将覆蓋在配置档案的預設值
$session = Session::forge( array('driver' => 'memcached', 'expiration_time' => 3600, 'memcached' = array('cookie_name' => 'appcookie')) );

請注意:當你想同時使用多个 session 驅动,这些驅动實例中的 cookie_name 必須是唯一的。 如果你试圖載入一个使用已经被使用的 cookie_name 的驅动,而且該實例使用與你嘗试載入的相同驅动,該實例会重複使用。 如果該實例使用不同的驅动,一个例外会被拋出。

rotate()

rotate 方法能让你手动強制一个 session id 的轉动。你可以使用它做为一个額外的安全對策,例如,當目前使用者的安全等級被修改時。

靜態
參数
回傳
範例
// 實例化一个資料庫 session
$session = Session::forge('db');

// 強制一个 session id 的轉动
$session->rotate();

get_config()

get_config 方法能檢索一个 session 驅动配置項目。

靜態
參数
參数 預設 描述
$name 必要 session 配置變数的名稱。
回傳 混合,請求的值,或 null 當請求變数不存在。
範例
// 實例化一个資料庫 session
$session = Session::forge('db');

// 取得定義的 session cookie 名稱
$cookiename = $session->get_config('cookie_name');

set_config()

set_config 方法能在执行階段改變一个 session 驅动配置項目。

靜態
參数
參数 預設 描述
$name 必要 session 配置變数的名稱。
回傳
範例
// 實例化一个資料庫 session
$session = Session::forge('db');

// 为此 session 設定逾期時間为 2 小時
$session->set_config('expiration_time', 7200);

在 Flash 使用 session

在網站中使用 Flash 物件的問題之一,是與你的 Web 应用程序互动時,他們不送回已儲存在瀏覽器的 cookie。 由於这个問題,就很難让他們明白應用程序的 session 狀態。

为了解決这个問題,Session 类別能让你使用一个 POST 變数,傳遞 cookie 到应用程序。 你可以使用 'post_cookie_name' 配置設定來設定該變数名稱。如果 Session 类別找到一个有此名稱的 $_POST 變数, 它将假設它包含 session cookie,并且不会使用該 session cookie。这能让你使用一點 javascript 來複製客戶端的 session cookie 的內容到該 POST 變数。

當你使用 Flash 为底的上傳程序時。你将必須設定 match_uafalse。 你必須这樣做,因为 Flash 使用一个不同的使用者代理, 这将阻止 Session 类別正確地識別使用者 session。

// 取得 session cookie 的函式
// 你可以使用自己的,或使用一个你喜愛的 javascript 框架提供的
function getCookie(c_name)
{
	if (document.cookie.length > 0)
	{
		c_start = document.cookie.indexOf(c_name + "=");

		if (c_start != -1)
		{
			c_start = c_start + c_name.length + 1;
			c_end = document.cookie.indexOf(";", c_start);
			if (c_end == -1) c_end = document.cookie.length;
			return unescape(document.cookie.substring(c_start, c_end));
		}
	}

	return "";
}

// 在这个範例中,我們正在使用 jquery 和 uploadify,而且我們正在
// formData 中傳遞 fuel cookie(这裡稱为 'fuelcid') 做为 'fuelcid'

// 注意:在產生此程式码時,不要硬寫 cookie 名稱,
// 而是從 session 配置档案取得 cookie 名稱。

// 注意:檢查下面的「無 cookie 的 session」段落
// 如果你沒有 cookie 可用來傳回 session id

// (參数與 Uploadify 3.2 版相關)

$(function()
{
	$('#custom_file_upload').uploadify(
	{
		'swf'            : '/uploadify/uploadify.swf',
		'uploader'       : '/uploadify/uploadify.php',
		'multi'          : true,
		'auto'           : true,
		'fileTypeExts'   : '*.jpg;*.gif;*.png',
		'fileTypeDesc'   : 'Image Files (.JPG, .GIF, .PNG)',
		'queueID'        : 'custom-queue',
		'queueSizeLimit' : 3,
		'removeCompleted': false,
		'formData'       : {'fuelcid': getCookie('fuelcid')},
		'onSelect'       : function(file)
		{
			alert('The file ' + file.name + ' was added to the queue.');
		},
		'onQueueComplete' : function(queueData)
		{
			alert(queueData.uploadsSuccessful + ' files were successfully uploaded.');
		}
	});
}

并行性

當論及 session、session cookie、和他們的行为,了解他們如何運作、 以及有什么可能性和侷限性是非常重要的。

當涉及到并行性時尤其如此。 對於 web 为底的应用程序,如果你在你的網頁上使用多个異步 ajax 呼叫,你会有并行性, 或如果你允許瀏覽器開啟多个相同应用程序的視窗(让我們面對它,你無法阻止)。

其他你需要知道的事是,預設情況下,session 类別会定期轉动(或重新產生)session id, 來防止由於 session id 固定的 session 劫持(有人偷取你的 session cookie 并用它來接手你的 session)。 你可以使用配置設定來控制轉动的時間,或甚至停用它,但從安全性的角度來看,这是个壞主意。 把这兩樣放在一起,你就有潛在的災難在手中!

一个實例:
- 你請求一个頁面,包含 ID-A 的 session cookie 被发送到伺服器。
- 你的頁面发送兩个 ajax 請求。包含 ID-A 的 session cookie 隨著每个請求再度被发送到伺服器。
- ajax 請求 1 完成,并轉动 ID。一个有 ID-B 的 cookie 被发送到瀏覽器。
- 现在 ajax 請求 2 完成。因为它发送相同的 cookie,它也判斷要旋轉,此時是 ID-C。
(你将取得一个不同的 ID,因为 session ID 是使用隨機演算法產生)

现在我們有个問題。session 类別嘗试更新儲存的 session 從 ID-A 到 ID-C,但它無法找到該 session。 請記住,它已经被第一个 ajax 呼叫從 ID-A 更新为 ID-B!所以它判斷該 session 無效,建立一个新的空 session, 并回傳該 cookie 到瀏覽器。现在你有效的 cookie 已经被新的空 session cookie 覆寫。 結果:你已经遺失 session。

这是一个大多数框架沒有解決的問題,進入 Fuel!

Fuel 的 session 类別包含兩个機制來偵测并減輕这个問題。 每个 session 鍵儲存包含兩个 session ID:目前 ID 及前一个 ID。 如果請求在 session id 剛被轉动之後進來,正確的 session 可以使用前一个儲存在鍵儲存區中的 session id 來定位。 并且在 session id 不能使用前一个 id 還原而不符合的情況下,沒有更新的 cookie 会被送回到瀏覽器。 結果是你遺失了該請求的 session 資料,但你不会遺失 session 本身。

無 cookie 的 session

在 session 資料變更後,Session 类別總是会產生一个 session cookie,并在 HTTP 回應表頭中发送它到客戶端。 然而,也有情況是不希望,甚至是不可能使用 cookie。 例如僅僅是因为客戶端不支援他們。

在这些情況下,有很多替代方案在請求中從客戶端傳遞 Session ID 回到应用程序:

你可以使用 Session::key() 來檢索目前的 session id,如此你可以在回應中傳遞它到客戶端, 而不須使用 cookie。如果你已经在你的 session 配置中配置了加密,你需要加密 id:

// 取回 session ID 并加密
$session_id = \Crypt::encode(\Session::key());