Session管理

3.4 版本
维护中的版本

Symfony的HttpFoundation组件有一个强力而灵活的session子系统,它被设计为通过一个“使用了多样化的session storage驱动”的简单面向对象接口来进行session管理。

Session的使用,靠的是实现了SessionInterface接口的Session类。

确保你的PHP原生session在使用Session类之前没有被启动。如果你的遗产级程序中的session系统启动了原生session,参考整合传统Session

快速示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
use Symfony\Component\HttpFoundation\Session\Session;
 
$session = new Session();
$session->start();
 
// set and get session attributes 设置和取得属性
$session->set('name', 'Drak');
$session->get('name');
 
// set flash messages 设置flash信息(提示条子)
$session->getFlashBag()->add('notice', 'Profile updated');
 
// retrieve messages 取出flash信息
foreach ($session->getFlashBag()->get('notice', array()) as $message) {
    echo '<div class="flash-notice">'.$message.'</div>';
}

Symfony的session在设计上是用来取代若干原生的PHP函数的。在程序中应该避免使用session_start()session_regenerate_id()session_id()session_namesession_destroy()等等,而使用下文中提到的API来替代之。

虽然显式地启动一个session是被推荐的,但session确实可以随需启动,也就是说,任何一个含有session的请求都可以对session数据进行读写。(译注:session在Symfony里是随取随用毋需特殊步骤的)。

Symfony的session与php.ini中的session.auto_start = 1是不兼容的。这个指令应该在php.ini中关掉,或者在服务器的指令(directive)中乃至.htaccess文件中关掉。

Session API 

Session类实现SessionInterface接口。

Session类有下面罗列的简单API,被归为三组:

Session工作流 

start()
启动session - 不要使用session_start()
migrate()
重新生成`session_id` - 不要使用session_regenerate_id()。本方法可选地(optionally)用于改变新cookie的lifetime(生命周期),调用这个方法时,cookie将被发出(emitted)。
invalidate()
清除所有session数据并且重新生成session ID - 不要使用session_destroy()
getId()
取得session ID - 不要使用session_id()
setId()
设置session ID - 不要使用session_id()
getName()
取得session名称 - 不要使用session_name()
setName()
设置session名称 - 不要使用session_name()

Session属性 

set()
用键(key)来设置属性。
get()
用键(key)来取得属性。
all()
取得键值对(key=>value)格式的所有属性。
has()
如果属性存在则返回true。
replace()
一次设置多个属性:接受一个键化数组(keyed array)然后设置每一个键值对(key=>value pair)。
remove()
用键(key)来删除一个属性。
clear()
清除所有属性。

这些属性,在内部被存放于一个“包(Bag)”中,Bag是个对象,但操作起来和数组类似。有一些方法专门用于“Bag”的管理:

registerBag()
注册一个SessionBagInterface接口(的实例)。
getBag()
使用bag name来取得一个SessionBagInterface接口(的实例)。
getFlashBag()
取得FlashBagInterface接口(的实例)。这只是getBag()的一个快捷方法。

Session元数据/Session Metadata 

getMetadataBag()
取得MetadataBag,内含sesion相关信息。

Session数据管理 

PHP的session管理需要使用$_SESSION超全局变量,但这在OOP程序中会干扰代码的可测试性以及封装。为了克服这种问题,Symfony使用了连接了session的session bag 来封装一组特定属性集或flash messages。

这种方法同时削弱了$_SESSION超全局变量中的命名空间污染(namespace pollution),这是因为每个bag把它所有的数据存放在一个独立的命名空间之下。这让Symfony能够与其他程序或类库“和平共处”,它们可能使用了$_SESSION超全局变量,但其所有数据都能与Symfony的session管理兼容。

Symfony提供了两种storage bags,分属两种不同的实现方法。因为是对应接口,所以在必要时,你可以扩展它们,或创建自己的bag类型。

SessionBagInterface有下列API,主要是为了满足内部使用:

getStorageKey()
返回bag最终存储的$_SESSION下面数组的键。这个值基本上可以保持其默认值作内部使用。
initialize()
被Symfony session storage类用于链接session bag里的数据。
getName()
返回session bag名称。

属性/Attributes 

属性包实现AttributeBagInterface接口的目的是要操作session属性的存储。这可能包括像是用户ID,以及“记住我”的登陆设置,或是其他基于用户状态的信息这一类东东。

AttributeBag
这个是标准的默认实现。
NamespacedAttributeBag
这个实现允许属性被存放于一个结构化的命名空间中。

因为每个键必须唯一,任何扁平的“键-值”存储系统都被局限在“对于复杂数据”的扩展性上。你可以对每个键引入命名约定来实现命名空间,以便你程序的不同部分能够互相操作而无有冲突。例如,module.foomodule2.foo。但是,当属性是数组时,这种方式不很便于实践,例如,对于一组tokens。这时,管理数组变成了负担,因为你不得不取出数组然后处理它,最后再存回去:

1
2
3
4
5
6
$tokens = array(
    'tokens' => array(
        'a' => 'a6c1e0b6',
        'b' => 'f4a7b1f3',
    ),
);

因此任何类似处理都可能很快变为难堪,甚至向数组中再添加一个token这种简单的动作也是如此:

1
2
3
$tokens = $session->get('tokens');
$tokens['c'] = $value;
$session->set('tokens', $tokens);

使用结构化命名空间,像下面这样使用一个namespace character(命名空间符号,默认是/),键可以被转换成数组结构:

1
$session->set('tokens/c', $value);

通过这种方式,你可以直接而轻松地访问到“属性数组”中的键。

AttributeBagInterface接口有以下简单API:

set()
用键(key)来设置属性。
get()
用键(key)来取得属性。
all()
取得键值对(key=>value)格式的所有属性。
has()
如果属性存在则返回true。
replace()
一次设置多个属性:接受一个键化数组(keyed array)然后设置每一个键值对(key=>value pair)。
remove()
用键(key)来删除一个属性。
clear()
清除所有属性。

Flash Messages(提示条子) 

FlashBagInterface接口的目的是要提供一种方式来设置和取出“基于当前session基本内容”中的信息。通常,它的工作流是在一次请求中设置flash messages,然后在页面跳转之后再显示这些(一般用作提示的)信息。比如,用户提交的表单触发了控制器的更新动作,处理完(表单数据)之后,控制器要跳转页面到“已更新的页面”或是“一个错误页”。Flash messages是在上一个页面请求中被设置(在session中)的,将被立即显示在下一个“已经加载了session”的页面中。这是仅用于flash messages的一个程序(接口)。

AutoExpireFlashBag
在这个实现中,被设置在某个“页面加载(page-load)”中的信息将能够在下一次的页面加载中显示出来。这些信息不管是否取出,都将自动失效。
FlashBag
在这个实现中,flash信息将被保留在session中,直到它们被显式地取出或清除。这让消息条子能够在ESI caching的条件下使用。

FlashBagInterface有如下简单API:

add()
向“某指定类型”的信息堆栈中增加一条flash信息(Adds a flash message to the stack of specified type)。
set()
通过“类型(type)”来设置flashes信息。本方法可以方便地取出一条stringarray(数组)方式取出。
get()
通过类型来取出flash信息,同时从FlashBag中清除这些信息。
setAll()
设置所有flashes信息,可接收键化数组的数组type => array(messages)
all()
取出所有flashes信息(以“键化数组的数组”来呈现),同时从FlashBag中清除所有信息。
peek()
通过类型(type)取出flashes信息(只读)。
peekAll()
取出所有flashes信息(只读),以“键化数组的数组”来呈现。
has()
如果类型(type)存在则返回true,否则返回flase。
keys()
取出(Bag中的)已存储的flash类型(flash types)之数组。
clear()
清除bag。

对于简单的程序,每个类型有一条flash信息足够了,例如,表单提交之后的确认信息。但是,flash信息被存入的是一个键化数组——flash的$type就是键——这表示你的程序可以对某给定类型提供多条信息。这让本API能够用在你程序中的复杂信息场景中:

1
2
3
4
5
6
7
8
9
10
11
12
use Symfony\Component\HttpFoundation\Session\Session;
 
$session = new Session();
$session->start();
 
// add flash messages 添加flash信息
$session->getFlashBag()->add(
    'warning',
    'Your config file is writable, it should be set read-only'
);
$session->getFlashBag()->add('error', 'Failed to update name');
$session->getFlashBag()->add('error', 'Another error');
1
2
3
4
5
6
7
8
9
// display warnings 显示警告
foreach ($session->getFlashBag()->get('warning', array()) as $message) {
    echo '<div class="flash-warning">'.$message.'</div>';
}
 
// display errors 显示错误
foreach ($session->getFlashBag()->get('error', array()) as $message) {
    echo '<div class="flash-error">'.$message.'</div>';
}
1
2
3
4
5
foreach ($session->getFlashBag()->all() as $type => $messages) {
    foreach ($messages as $message) {
        echo '<div class="flash-'.$type.'">'.$message.'</div>';
    }
}

本文,包括例程代码在内,采用的是 Creative Commons BY-SA 3.0 创作共用授权。

登录symfonychina 发表评论或留下问题(我们会尽量回复)