如何使用PdoSessionHandler把session存储到数据库中

3.4 版本
维护中的版本

默认的Symfony session存储,是把session信息写入到文件中。多数中到大型网站使用一个数据库来存储session值而是不使用文件,这是因为在“服务器集群”环境下数据库更容易使用和升级。

对于数据库的session存储,Symfony提供一个内置的解决方案叫做 PdoSessionHandler。为了使用它,你只需在主配置文件中改变一些参数:

1
2
3
4
5
6
7
8
9
10
11
12
13
# app/config/config.yml
framework:
    session:
        # ...
        handler_id: session.handler.pdo

services:
    session.handler.pdo:
        class:     Symfony\Component\HttpFoundation\Session\Storage\Handler\PdoSessionHandler
        public:    false
        arguments:
            - 'mysql:dbname=mydatabase'
            - { db_username: myuser, db_password: mypassword }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
<!-- app/config/config.xml -->
<framework:config>
    <framework:session handler-id="session.handler.pdo" cookie-lifetime="3600" auto-start="true"/>
</framework:config>
 
<services>
    <service id="session.handler.pdo" class="Symfony\Component\HttpFoundation\Session\Storage\Handler\PdoSessionHandler" public="false">
        <argument>mysql:dbname=mydatabase</agruement>
        <argument type="collection">
            <argument key="db_username">myuser</argument>
            <argument key="db_password">mypassword</argument>
        </argument>
    </service>
</services>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// app/config/config.php
use Symfony\Component\DependencyInjection\Definition;
use Symfony\Component\DependencyInjection\Reference;
 
$container->loadFromExtension('framework', array(
    ...,
    'session' => array(
        // ...,
        'handler_id' => 'session.handler.pdo',
    ),
));
 
$storageDefinition = new Definition('Symfony\Component\HttpFoundation\Session\Storage\Handler\PdoSessionHandler', array(
    'mysql:dbname=mydatabase',
    array('db_username' => 'myuser', 'db_password' => 'mypassword')
));
$container->setDefinition('session.handler.pdo', $storageDefinition);

配置表名和列名 

这将预期一个sessions表以及一些不同的列。表名,和所有的列名,都可以被配置,通过向PdoSessionHandler传入二维数组的参数即可:

1
2
3
4
5
6
7
8
9
# app/config/config.yml
services:
    # ...
    session.handler.pdo:
        class:     Symfony\Component\HttpFoundation\Session\Storage\Handler\PdoSessionHandler
        public:    false
        arguments:
            - 'mysql:dbname=mydatabase'
            - { db_table: sessions, db_username: myuser, db_password: mypassword }
1
2
3
4
5
6
7
8
9
10
11
<!-- app/config/config.xml -->
<services>
    <service id="session.handler.pdo" class="Symfony\Component\HttpFoundation\Session\Storage\Handler\PdoSessionHandler" public="false">
        <argument>mysql:dbname=mydatabase</agruement>
        <argument type="collection">
            <argument key="db_table">sessions</argument>
            <argument key="db_username">myuser</argument>
            <argument key="db_password">mypassword</argument>
        </argument>
    </service>
</services>
1
2
3
4
5
6
7
8
9
10
// app/config/config.php
 
use Symfony\Component\DependencyInjection\Definition;
// ...
 
$storageDefinition = new Definition('Symfony\Component\HttpFoundation\Session\Storage\Handler\PdoSessionHandler', array(
    'mysql:dbname=mydatabase',
    array('db_table' => 'sessions', 'db_username' => 'myuser', 'db_password' => 'mypassword')
));
$container->setDefinition('session.handler.pdo', $storageDefinition);

以下是你必须要配置的参数(键):

db_talbe(默认是sessions):

你的数据库session表的名字;


db_id_col(默认是sess_id):

你的session表的id字段的名字(VARCHAR(128));


db_data_col(默认是sess_data):

你的session表的value字段的名字(BLOB);


db_time_col(默认是sess_time):

你的session表的字段的名字(INTEGER);


db_lifetime_col(默认是sess_lifetime):

你的session表的lifetime字段的名字(INTEGER);

分享你的数据库连接信息 

根据给出的配置,数据库的连接设置(database connection settings)仅能被定义为session storage连接。这在当你使用一个用于session数据的独立数据库时是没有问题的。

但如果你希望把session数据存到“与你的项目数据所在”相同的数据库的话,你可以使用parameters.yml中的连接信息,通过引用定义在那里的database相关参数即可:

1
2
3
4
5
6
7
services:
    session.handler.pdo:
        class:     Symfony\Component\HttpFoundation\Session\Storage\Handler\PdoSessionHandler
        public:    false
        arguments:
            - 'mysql:host=%database_host%;port=%database_port%;dbname=%database_name%'
            - { db_username: '%database_user%', db_password: '%database_password%' }
1
2
3
4
5
6
7
<service id="session.handler.pdo" class="Symfony\Component\HttpFoundation\Session\Storage\Handler\PdoSessionHandler" public="false">
    <argument>mysql:host=%database_host%;port=%database_port%;dbname=%database_name%</argument>
    <argument type="collection">
        <argument key="db_username">%database_user%</argument>
        <argument key="db_password">%database_password%</argument>
    </argument>
</service>
1
2
3
4
$storageDefinition = new Definition('Symfony\Component\HttpFoundation\Session\Storage\Handler\PdoSessionHandler', array(
    'mysql:host=%database_host%;port=%database_port%;dbname=%database_name%',
    array('db_username' => '%database_user%', 'db_password' => '%database_password%')
));

准备数据库以存储Session 

在把session存入数据库之前,你必须创建用来存放信息的表。下面小节包含了一些SQL声明的样例,你可以用在自己特定的数据库引擎中。

MySQL 

1
2
3
4
5
6
CREATE TABLE `sessions` (
    `sess_id` VARBINARY(128) NOT NULL PRIMARY KEY,
    `sess_data` BLOB NOT NULL,
    `sess_time` INTEGER UNSIGNED NOT NULL,
    `sess_lifetime` MEDIUMINT NOT NULL
) COLLATE utf8_bin, ENGINE = InnoDB;

BLOB字段类型只能存放64kb的内容。如果存于用户session中的数据超出此值,会有异常抛出,或者用户的session会被静默重置。如果有容量需求可以考虑使用MEDIUMBLOG字段。

PostgreSQL 

1
2
3
4
5
CREATE TABLE sessions (
    sess_id VARCHAR(128) NOT NULL PRIMARY KEY,
    sess_data BYTEA NOT NULL,
    sess_time INTEGER NOT NULL,
    sess_lifetime INTEGER NOT NULL

Microsoft SQL Server 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
CREATE TABLE [dbo].[sessions](
    [sess_id] [nvarchar](255) NOT NULL,
    [sess_data] [ntext] NOT NULL,
    [sess_time] [INT] NOT NULL,
    [sess_lifetime] [INT] NOT NULL,
    PRIMARY KEY CLUSTERED(
        [sess_id] ASC
    ) WITH (
        PAD_INDEX  = OFF,
        STATISTICS_NORECOMPUTE  = OFF,
        IGNORE_DUP_KEY = OFF,
        ALLOW_ROW_LOCKS  = ON,
        ALLOW_PAGE_LOCKS  = ON
    ) ON [PRIMARY]
) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]

如果session数据不适合存入data字段,它可能会被数据库引擎给清除。更糟的是,当session数据挂掉之后,PHP引擎直接忽略数据而不给予警告。

如果程序存储了大量的session data,这个问题可以靠“提升字段容量(使用BLOBMEDIUMBLOG)”来解决。当使用MySQL作为数据库引擎时,你可以同时开启strict SQL mode(严格SQL模式),以便在类似错误发生时,能够收到通知。

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

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