Как использовать PdoSessionHandler для хранения сессий в БД

Хранилище сессий Symfony по умолчанию сохраняет информацию о сессиях в файлы. Большинство средних и больших веб-сайтов используют БД для хранения значений сессии, вмето файлов, так как базы данных легче использовать и масштабировать в окружении множества веб-серверов.

Symfony имеет встроенное решение для хранения сессий в базах данных, под названием PdoSessionHandler. Чтобы использовать его, зарегистрируйте новый сервис-обработчик:

  • YAML
     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    # config/services.yaml
    services:
        # ...
    
        Symfony\Component\HttpFoundation\Session\Storage\Handler\PdoSessionHandler:
            arguments:
                - 'mysql:dbname=mydatabase'
                - { db_username: myuser, db_password: mypassword }
    
                # Если вы используете Doctrine и хотите использовать это соединение повторно, тогд:
                # прокомментируйте 2 строчки выше, и уберите комментарий из строчки ниже
                # - !service { class: PDO, factory: 'database_connection:getWrappedConnection' }
    
  • XML
     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    <!-- config/services.xml -->
    <?xml version="1.0" encoding="UTF-8" ?>
    <container xmlns="http://symfony.com/schema/dic/services"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xmlns:framework="http://symfony.com/schema/dic/symfony"
        xsi:schemaLocation="http://symfony.com/schema/dic/services
            http://symfony.com/schema/dic/services/services-1.0.xsd
            http://symfony.com/schema/dic/symfony/symfony-1.0.xsd">
    
        <services>
            <service id="Symfony\Component\HttpFoundation\Session\Storage\Handler\PdoSessionHandler" public="false">
                <argument>mysql:dbname=mydatabase</argument>
                <argument type="collection">
                    <argument key="db_username">myuser</argument>
                    <argument key="db_password">mypassword</argument>
                </argument>
            </service>
        </services>
    </container>
    
  • PHP
    1
    2
    3
    4
    5
    6
    7
    8
    9
    // config/services.php
    use Symfony\Component\HttpFoundation\Session\Storage\Handler\PdoSessionHandler;
    
    $storageDefinition = $container->autowire(PdoSessionHandler::class)
        ->setArguments(array(
            'mysql:dbname=mydatabase',
            array('db_username' => 'myuser', 'db_password' => 'mypassword')
        ))
    ;
    

Далее, скажите Symfony использовать ваш сервис в качестве обработчика сессии:

  • YAML
    1
    2
    3
    4
    5
    # config/packages/framework.yaml
    framework:
        session:
            # ...
            handler_id: Symfony\Component\HttpFoundation\Session\Storage\Handler\PdoSessionHandler
    
  • XML
    1
    2
    3
    4
    5
    <!-- config/packages/framework.xml -->
    <framework:config>
        <!-- ... -->
        <framework:session handler-id="Symfony\Component\HttpFoundation\Session\Storage\Handler\PdoSessionHandler" cookie-lifetime="3600" auto-start="true"/>
    </framework:config>
    
  • PHP
     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    // config/packages/framework.php
    use Symfony\Component\HttpFoundation\Session\Storage\Handler\PdoSessionHandler;
    
    // ...
    $container->loadFromExtension('framework', array(
        // ...
        'session' => array(
            // ...
            'handler_id' => PdoSessionHandler::class,
        ),
    ));
    

Конфигурация имён таблицы и колонок

Для этого необходима таблица sessions с некоторым количеством разных столбцов. Имя таблицы, а также все имена стобцов, могут быть сконфигурированы путём передачи второго массива аргументов в PdoSessionHandler:

  • YAML
    1
    2
    3
    4
    5
    6
    7
    8
    # config/services.yaml
    services:
        # ...
    
        Symfony\Component\HttpFoundation\Session\Storage\Handler\PdoSessionHandler:
            arguments:
                - 'mysql:dbname=mydatabase'
                - { db_table: sessions, db_username: myuser, db_password: mypassword }
    
  • XML
     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    <!-- config/services.xml -->
    <?xml version="1.0" encoding="UTF-8" ?>
    <container xmlns="http://symfony.com/schema/dic/services"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://symfony.com/schema/dic/services
            http://symfony.com/schema/dic/services/services-1.0.xsd">
    
        <services>
            <service id="Symfony\Component\HttpFoundation\Session\Storage\Handler\PdoSessionHandler" public="false">
                <argument>mysql:dbname=mydatabase</argument>
                <argument type="collection">
                    <argument key="db_table">sessions</argument>
                    <argument key="db_username">myuser</argument>
                    <argument key="db_password">mypassword</argument>
                </argument>
            </service>
        </services>
    </container>
    
  • PHP
     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    // config/services.php
    
    use Symfony\Component\HttpFoundation\Session\Storage\Handler\PdoSessionHandler;
    // ...
    
    $container->autowire(PdoSessionHandler::class)
        ->setArguments(array(
            'mysql:dbname=mydatabase',
            array('db_table' => 'sessions', 'db_username' => 'myuser', 'db_password' => 'mypassword')
        ))
    ;
    

Вот параметры, которые вы можете сконфигурировать:

db_table (по умолчанию sessions):
Имя таблицы сессии в вашей БД;
db_id_col (по умолчанию sess_id):
Имя столбца id в вашей талбице сессии (VARCHAR(128));
db_data_col (по умолчанию sess_data):
Имя столбца значения в вашей таблице сессии (BLOB);
db_time_col (по умолчанию sess_time):
Имя столбца времени в вашей таблице сессии (INTEGER);
db_lifetime_col (по умолчанию sess_lifetime):
Имя столбца жизненного цикла в вашей таблице сессии (INTEGER).

Подготовка БД для сохранения сессий

Перед тем, как сохранять сессии в БД, вы должны создать таблицу, которая будет хранить информацию. Обработчик сессий предоставляет метод под названием createTable() для установки этой таблицы за вас, в соответствии с используемым движком БД:

1
2
3
4
5
try {
    $sessionHandlerService->createTable();
} catch (\PDOException $e) {
    // таблица не могла быть создана по какой-то причине
}

Если вы предпочитаете устанавливать таблицу самостоятельно, то вот некоторые примеры утверждений SQL, которые вы можете использовать, в соответствии с вашим конкретным движком БД.

Отличным способом запустить это в производстве будет сгенерировать пустую миграцию, а потом добавить внутрь этот SQL:

1
$ php bin/console doctrine:migrations:generate

Найдите правильный SQL ниже и поместите его внутрь этого файла. Далее, выполните его с:

1
$ php bin/console doctrine:migrations:migrate

MySQL

1
2
3
4
5
6
CREATE TABLE `sessions` (
    `sess_id` VARCHAR(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;

Note

Тип столбца BLOB может хранить максимум 64 килобита. Если данные, сохраняемые в сессии пользователя, превысят это значение, может быть вызвано исключение, или сессия будет тихо восстановлена. Рассмотрите использование MEDIUMBLOB, если вам нужно больше места.

PostgreSQL

1
2
3
4
5
6
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

 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]

Caution

Если данные сессии не помещаются в столбце данных, они могут быть усечены движком базы данных. Чтобы усугубить ситуацию, когда данные сессии искажаются, PHP начинает игнорировать данные, не предупреждая об этом.

Если приложение хранит большие объёмы данных сессии, эта проблема может быть решена путём увеличения размера столбца (используйте BLOB или даже MEDIUMBLOB). При использовании MySQL в качестве движка БД, вы можете также активировать режим строгого SQL, чтобы получать уведомления, когда будет происходить такая ошибка.

Эта документация является переводом официальной документации Symfony и предоставляется по свободной лицензии CC BY-SA 3.0.