mirror of
https://github.com/symfony/symfony-docs.git
synced 2026-03-23 16:22:10 +01:00
* 6.4: Minor reword Add a note that checking for flash messages defeats HTTP cacheability
1900 lines
70 KiB
ReStructuredText
1900 lines
70 KiB
ReStructuredText
Sessions
|
|
========
|
|
|
|
The Symfony HttpFoundation component has a very powerful and flexible session
|
|
subsystem which is designed to provide session management that you can use to
|
|
store information about the user between requests through a clear
|
|
object-oriented interface using a variety of session storage drivers.
|
|
|
|
Symfony sessions are designed to replace the usage of the ``$_SESSION`` super
|
|
global and native PHP functions related to manipulating the session like
|
|
``session_start()``, ``session_regenerate_id()``, ``session_id()``,
|
|
``session_name()``, and ``session_destroy()``.
|
|
|
|
.. note::
|
|
|
|
Sessions are only started if you read or write from it.
|
|
|
|
Installation
|
|
------------
|
|
|
|
You need to install the HttpFoundation component to handle sessions:
|
|
|
|
.. code-block:: terminal
|
|
|
|
$ composer require symfony/http-foundation
|
|
|
|
.. _session-intro:
|
|
|
|
Basic Usage
|
|
-----------
|
|
|
|
The session is available through the ``Request`` object and the ``RequestStack``
|
|
service. Symfony injects the ``request_stack`` service in services and controllers
|
|
if you type-hint an argument with :class:`Symfony\\Component\\HttpFoundation\\RequestStack`::
|
|
|
|
.. configuration-block::
|
|
|
|
.. code-block:: php-symfony
|
|
|
|
use Symfony\Component\HttpFoundation\RequestStack;
|
|
|
|
class SomeService
|
|
{
|
|
public function __construct(
|
|
private RequestStack $requestStack,
|
|
) {
|
|
// Accessing the session in the constructor is *NOT* recommended, since
|
|
// it might not be accessible yet or lead to unwanted side-effects
|
|
// $this->session = $requestStack->getSession();
|
|
}
|
|
|
|
public function someMethod(): void
|
|
{
|
|
$session = $this->requestStack->getSession();
|
|
|
|
// ...
|
|
}
|
|
}
|
|
|
|
.. code-block:: php-standalone
|
|
|
|
use Symfony\Component\HttpFoundation\Session\Session;
|
|
|
|
$session = new Session();
|
|
$session->start();
|
|
|
|
From a Symfony controller, you can also type-hint an argument with
|
|
:class:`Symfony\\Component\\HttpFoundation\\Request`::
|
|
|
|
use Symfony\Component\HttpFoundation\Request;
|
|
use Symfony\Component\HttpFoundation\Response;
|
|
|
|
public function index(Request $request): Response
|
|
{
|
|
$session = $request->getSession();
|
|
|
|
// ...
|
|
}
|
|
|
|
Session Attributes
|
|
------------------
|
|
|
|
PHP's session management requires the use of the ``$_SESSION`` super-global.
|
|
However, this interferes with code testability and encapsulation in an OOP
|
|
paradigm. To help overcome this, Symfony uses *session bags* linked to the
|
|
session to encapsulate a specific dataset of **attributes**.
|
|
|
|
This approach mitigates namespace pollution within the ``$_SESSION``
|
|
super-global because each bag stores all its data under a unique namespace.
|
|
This allows Symfony to peacefully co-exist with other applications or libraries
|
|
that might use the ``$_SESSION`` super-global and all data remains completely
|
|
compatible with Symfony's session management.
|
|
|
|
A session bag is a PHP object that acts like an array::
|
|
|
|
// stores an attribute for reuse during a later user request
|
|
$session->set('attribute-name', 'attribute-value');
|
|
|
|
// gets an attribute by name
|
|
$foo = $session->get('foo');
|
|
|
|
// the second argument is the value returned when the attribute doesn't exist
|
|
$filters = $session->get('filters', []);
|
|
|
|
Stored attributes remain in the session for the remainder of that user's session.
|
|
By default, session attributes are key-value pairs managed with the
|
|
:class:`Symfony\\Component\\HttpFoundation\\Session\\Attribute\\AttributeBag`
|
|
class.
|
|
|
|
Sessions are automatically started whenever you read, write or even check for
|
|
the existence of data in the session. This may hurt your application performance
|
|
because all users will receive a session cookie. In order to prevent starting
|
|
sessions for anonymous users, you must *completely* avoid accessing the session.
|
|
|
|
.. note::
|
|
|
|
Sessions will also be started when using features that rely on them internally,
|
|
such as the :ref:`stateful CSRF protection in forms <csrf-protection-forms>`.
|
|
|
|
.. _flash-messages:
|
|
|
|
Flash Messages
|
|
--------------
|
|
|
|
You can store special messages, called "flash" messages, on the user's session.
|
|
By design, flash messages are meant to be used exactly once: they vanish from
|
|
the session automatically as soon as you retrieve them. This feature makes
|
|
"flash" messages particularly great for storing user notifications.
|
|
|
|
For example, imagine you're processing a :doc:`form </forms>` submission::
|
|
|
|
.. configuration-block::
|
|
|
|
.. code-block:: php-symfony
|
|
|
|
use Symfony\Component\HttpFoundation\Request;
|
|
use Symfony\Component\HttpFoundation\Response;
|
|
// ...
|
|
|
|
public function update(Request $request): Response
|
|
{
|
|
// ...
|
|
|
|
if ($form->isSubmitted() && $form->isValid()) {
|
|
// do some sort of processing
|
|
|
|
$this->addFlash(
|
|
'notice',
|
|
'Your changes were saved!'
|
|
);
|
|
// $this->addFlash() is equivalent to $request->getSession()->getFlashBag()->add()
|
|
|
|
return $this->redirectToRoute(/* ... */);
|
|
}
|
|
|
|
return $this->render(/* ... */);
|
|
}
|
|
|
|
.. code-block:: php-standalone
|
|
|
|
use Symfony\Component\HttpFoundation\Session\Session;
|
|
|
|
$session = new Session();
|
|
$session->start();
|
|
|
|
// retrieve the flash messages bag
|
|
$flashes = $session->getFlashBag();
|
|
|
|
// add flash messages
|
|
$flashes->add(
|
|
'notice',
|
|
'Your changes were saved'
|
|
);
|
|
|
|
After processing the request, the controller sets a flash message in the
|
|
session and then redirects. The message key (``notice`` in this example)
|
|
can be anything. You'll use this key to retrieve the message.
|
|
|
|
In the template of the next page (or even better, in your base layout template),
|
|
read any flash messages from the session using the ``flashes()`` method provided
|
|
by the :ref:`Twig global app variable <twig-app-variable>`.
|
|
Alternatively, you can use the
|
|
:method:`Symfony\\Component\\HttpFoundation\\Session\\Flash\\FlashBagInterface::peek`
|
|
method to retrieve the message while keeping it in the bag:
|
|
|
|
.. configuration-block::
|
|
|
|
.. code-block:: html+twig
|
|
|
|
{# templates/base.html.twig #}
|
|
|
|
{# read and display just one flash message type #}
|
|
{% for message in app.flashes('notice') %}
|
|
<div class="flash-notice">
|
|
{{ message }}
|
|
</div>
|
|
{% endfor %}
|
|
|
|
{# same but without clearing them from the flash bag #}
|
|
{% for message in app.session.flashbag.peek('notice') %}
|
|
<div class="flash-notice">
|
|
{{ message }}
|
|
</div>
|
|
{% endfor %}
|
|
|
|
{# read and display several types of flash messages #}
|
|
{% for label, messages in app.flashes(['success', 'warning']) %}
|
|
{% for message in messages %}
|
|
<div class="flash-{{ label }}">
|
|
{{ message }}
|
|
</div>
|
|
{% endfor %}
|
|
{% endfor %}
|
|
|
|
{# read and display all flash messages #}
|
|
{% for label, messages in app.flashes %}
|
|
{% for message in messages %}
|
|
<div class="flash-{{ label }}">
|
|
{{ message }}
|
|
</div>
|
|
{% endfor %}
|
|
{% endfor %}
|
|
|
|
{# or without clearing the flash bag #}
|
|
{% for label, messages in app.session.flashbag.peekAll() %}
|
|
{% for message in messages %}
|
|
<div class="flash-{{ label }}">
|
|
{{ message }}
|
|
</div>
|
|
{% endfor %}
|
|
{% endfor %}
|
|
|
|
.. code-block:: php-standalone
|
|
|
|
// display warnings
|
|
foreach ($session->getFlashBag()->get('warning', []) as $message) {
|
|
echo '<div class="flash-warning">'.$message.'</div>';
|
|
}
|
|
|
|
// display warnings without clearing them from the flash bag
|
|
foreach ($session->getFlashBag()->peek('warning', []) as $message) {
|
|
echo '<div class="flash-warning">'.$message.'</div>';
|
|
}
|
|
|
|
// display errors
|
|
foreach ($session->getFlashBag()->get('error', []) as $message) {
|
|
echo '<div class="flash-error">'.$message.'</div>';
|
|
}
|
|
|
|
// display all flashes at once
|
|
foreach ($session->getFlashBag()->all() as $type => $messages) {
|
|
foreach ($messages as $message) {
|
|
echo '<div class="flash-'.$type.'">'.$message.'</div>';
|
|
}
|
|
}
|
|
|
|
// display all flashes at once without clearing the flash bag
|
|
foreach ($session->getFlashBag()->peekAll() as $type => $messages) {
|
|
foreach ($messages as $message) {
|
|
echo '<div class="flash-'.$type.'">'.$message.'</div>';
|
|
}
|
|
}
|
|
|
|
It's common to use ``notice``, ``warning`` and ``error`` as the keys of the
|
|
different types of flash messages, but you can use any key that fits your
|
|
needs.
|
|
|
|
.. tip::
|
|
|
|
Accessing flash messages requires starting the session, which in turn
|
|
causes Symfony to mark the response as ``private``. In general, because
|
|
flash messages are meant to be displayed only once, pages that might show
|
|
them cannot reasonably be cached by HTTP caches.
|
|
|
|
As an alternative, you can load flash messages asynchronously through
|
|
another HTTP request (for example, using a `Twig Live Component`_), making
|
|
the original page fully cacheable.
|
|
|
|
Configuration
|
|
-------------
|
|
|
|
In the Symfony framework, sessions are enabled by default. Session storage and
|
|
other configuration can be controlled under the :ref:`framework.session
|
|
configuration <config-framework-session>` in
|
|
``config/packages/framework.yaml``:
|
|
|
|
.. configuration-block::
|
|
|
|
.. code-block:: yaml
|
|
|
|
# config/packages/framework.yaml
|
|
framework:
|
|
# Enables session support. Note that the session will ONLY be started if you read or write from it.
|
|
# Remove or comment this section to explicitly disable session support.
|
|
session:
|
|
# ID of the service used for session storage
|
|
# NULL means that Symfony uses PHP default session mechanism
|
|
handler_id: null
|
|
# improves the security of the cookies used for sessions
|
|
cookie_secure: auto
|
|
cookie_samesite: lax
|
|
storage_factory_id: session.storage.factory.native
|
|
|
|
.. code-block:: xml
|
|
|
|
<!-- config/packages/framework.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
|
|
https://symfony.com/schema/dic/services/services-1.0.xsd
|
|
http://symfony.com/schema/dic/symfony https://symfony.com/schema/dic/symfony/symfony-1.0.xsd">
|
|
|
|
<framework:config>
|
|
<!--
|
|
Enables session support. Note that the session will ONLY be started if you read or write from it.
|
|
Remove or comment this section to explicitly disable session support.
|
|
handler-id: ID of the service used for session storage
|
|
NULL means that Symfony uses PHP default session mechanism
|
|
cookie-secure and cookie-samesite: improves the security of the cookies used for sessions
|
|
-->
|
|
<framework:session handler-id="null"
|
|
cookie-secure="auto"
|
|
cookie-samesite="lax"
|
|
storage_factory_id="session.storage.factory.native"/>
|
|
</framework:config>
|
|
</container>
|
|
|
|
.. code-block:: php
|
|
|
|
// config/packages/framework.php
|
|
use Symfony\Component\HttpFoundation\Cookie;
|
|
use Symfony\Config\FrameworkConfig;
|
|
|
|
return static function (FrameworkConfig $framework): void {
|
|
$framework->session()
|
|
// Enables session support. Note that the session will ONLY be started if you read or write from it.
|
|
// Remove or comment this section to explicitly disable session support.
|
|
->enabled(true)
|
|
// ID of the service used for session storage
|
|
// NULL means that Symfony uses PHP default session mechanism
|
|
->handlerId(null)
|
|
// improves the security of the cookies used for sessions
|
|
->cookieSecure('auto')
|
|
->cookieSamesite(Cookie::SAMESITE_LAX)
|
|
->storageFactoryId('session.storage.factory.native')
|
|
;
|
|
};
|
|
|
|
.. code-block:: php-standalone
|
|
|
|
use Symfony\Component\HttpFoundation\Cookie;
|
|
use Symfony\Component\HttpFoundation\Session\Attribute\AttributeBag;
|
|
use Symfony\Component\HttpFoundation\Session\Session;
|
|
use Symfony\Component\HttpFoundation\Session\Storage\NativeSessionStorage;
|
|
|
|
$storage = new NativeSessionStorage([
|
|
'cookie_secure' => 'auto',
|
|
'cookie_samesite' => Cookie::SAMESITE_LAX,
|
|
]);
|
|
$session = new Session($storage);
|
|
|
|
Setting the ``handler_id`` config option to ``null`` means that Symfony will
|
|
use the native PHP session mechanism. The session metadata files will be stored
|
|
outside of the Symfony application, in a directory controlled by PHP. Although
|
|
this usually simplifies things, some session expiration related options may not
|
|
work as expected if other applications that write to the same directory have
|
|
short max lifetime settings.
|
|
|
|
If you prefer, you can use the ``session.handler.native_file`` service as
|
|
``handler_id`` to let Symfony manage the sessions itself. Another useful option
|
|
is ``save_path``, which defines the directory where Symfony will store the
|
|
session metadata files:
|
|
|
|
.. configuration-block::
|
|
|
|
.. code-block:: yaml
|
|
|
|
# config/packages/framework.yaml
|
|
framework:
|
|
session:
|
|
# ...
|
|
handler_id: 'session.handler.native_file'
|
|
save_path: '%kernel.project_dir%/var/sessions/%kernel.environment%'
|
|
|
|
.. code-block:: xml
|
|
|
|
<!-- config/packages/framework.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
|
|
https://symfony.com/schema/dic/services/services-1.0.xsd
|
|
http://symfony.com/schema/dic/symfony https://symfony.com/schema/dic/symfony/symfony-1.0.xsd">
|
|
|
|
<framework:config>
|
|
<framework:session enabled="true"
|
|
handler-id="session.handler.native_file"
|
|
save-path="%kernel.project_dir%/var/sessions/%kernel.environment%"/>
|
|
</framework:config>
|
|
</container>
|
|
|
|
.. code-block:: php
|
|
|
|
// config/packages/framework.php
|
|
use Symfony\Config\FrameworkConfig;
|
|
|
|
return static function (FrameworkConfig $framework): void {
|
|
$framework->session()
|
|
// ...
|
|
->handlerId('session.handler.native_file')
|
|
->savePath('%kernel.project_dir%/var/sessions/%kernel.environment%')
|
|
;
|
|
};
|
|
|
|
.. code-block:: php-standalone
|
|
|
|
use Symfony\Component\HttpFoundation\Cookie;
|
|
use Symfony\Component\HttpFoundation\Session\Attribute\AttributeBag;
|
|
use Symfony\Component\HttpFoundation\Session\Session;
|
|
use Symfony\Component\HttpFoundation\Session\Storage\Handler\NativeFileSessionHandler;
|
|
use Symfony\Component\HttpFoundation\Session\Storage\NativeSessionStorage;
|
|
|
|
$handler = new NativeFileSessionHandler('/var/sessions');
|
|
$storage = new NativeSessionStorage([], $handler);
|
|
$session = new Session($storage);
|
|
|
|
Check out the Symfony config reference to learn more about the other available
|
|
:ref:`Session configuration options <config-framework-session>`.
|
|
|
|
.. warning::
|
|
|
|
Symfony sessions are incompatible with ``php.ini`` directive
|
|
``session.auto_start = 1`` This directive should be turned off in
|
|
``php.ini``, in the web server directives or in ``.htaccess``.
|
|
|
|
.. deprecated:: 7.2
|
|
|
|
The ``sid_length`` and ``sid_bits_per_character`` options were deprecated
|
|
in Symfony 7.2 and will be ignored in Symfony 8.0.
|
|
|
|
The session cookie is also available in :ref:`the Response object <component-http-foundation-response>`.
|
|
This is useful to get that cookie in the CLI context or when using PHP runners
|
|
like Roadrunner or Swoole.
|
|
|
|
Session Idle Time/Keep Alive
|
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
|
|
There are often circumstances where you may want to protect, or minimize
|
|
unauthorized use of a session when a user steps away from their terminal while
|
|
logged in by destroying the session after a certain period of idle time. For
|
|
example, it is common for banking applications to log the user out after just
|
|
5 to 10 minutes of inactivity. Setting the cookie lifetime here is not
|
|
appropriate because that can be manipulated by the client, so we must do the expiry
|
|
on the server side. The easiest way is to implement this via :ref:`session garbage collection <session-garbage-collection>`
|
|
which runs reasonably frequently. The ``cookie_lifetime`` would be set to a
|
|
relatively high value, and the garbage collection ``gc_maxlifetime`` would be set
|
|
to destroy sessions at whatever the desired idle period is.
|
|
|
|
The other option is specifically check if a session has expired after the
|
|
session is started. The session can be destroyed as required. This method of
|
|
processing can allow the expiry of sessions to be integrated into the user
|
|
experience, for example, by displaying a message.
|
|
|
|
Symfony records some metadata about each session to give you fine control over
|
|
the security settings::
|
|
|
|
$session->getMetadataBag()->getCreated();
|
|
$session->getMetadataBag()->getLastUsed();
|
|
|
|
Both methods return a Unix timestamp (relative to the server).
|
|
|
|
This metadata can be used to explicitly expire a session on access::
|
|
|
|
$session->start();
|
|
if (time() - $session->getMetadataBag()->getLastUsed() > $maxIdleTime) {
|
|
$session->invalidate();
|
|
throw new SessionExpired(); // redirect to expired session page
|
|
}
|
|
|
|
It is also possible to tell what the ``cookie_lifetime`` was set to for a
|
|
particular cookie by reading the ``getLifetime()`` method::
|
|
|
|
$session->getMetadataBag()->getLifetime();
|
|
|
|
The expiry time of the cookie can be determined by adding the created
|
|
timestamp and the lifetime.
|
|
|
|
.. _session-garbage-collection:
|
|
|
|
Configuring Garbage Collection
|
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
|
|
When a session opens, PHP will call the ``gc`` handler randomly according to the
|
|
probability set by ``session.gc_probability`` / ``session.gc_divisor``. For
|
|
example if these were set to ``5/100`` respectively, it would mean a probability
|
|
of 5%. Similarly, ``3/4`` would mean a 3 in 4 chance of being called, i.e. 75%.
|
|
|
|
If the garbage collection handler is invoked, PHP will pass the value stored in
|
|
the ``php.ini`` directive ``session.gc_maxlifetime``. The meaning in this context is
|
|
that any stored session that was saved more than ``gc_maxlifetime`` ago should be
|
|
deleted. This allows one to expire records based on idle time.
|
|
|
|
However, some operating systems (e.g. Debian) manage session handling differently
|
|
and set the ``session.gc_probability`` variable to ``0`` to prevent PHP from performing
|
|
garbage collection. By default, Symfony uses the value of the ``gc_probability``
|
|
directive set in the ``php.ini`` file. If you can't modify this PHP setting, you
|
|
can configure it directly in Symfony:
|
|
|
|
.. code-block:: yaml
|
|
|
|
# config/packages/framework.yaml
|
|
framework:
|
|
session:
|
|
# ...
|
|
gc_probability: 1
|
|
|
|
Alternatively, you can configure these settings by passing ``gc_probability``,
|
|
``gc_divisor`` and ``gc_maxlifetime`` in an array to the constructor of
|
|
:class:`Symfony\\Component\\HttpFoundation\\Session\\Storage\\NativeSessionStorage`
|
|
or to the :method:`Symfony\\Component\\HttpFoundation\\Session\\Storage\\NativeSessionStorage::setOptions`
|
|
method.
|
|
|
|
.. versionadded:: 7.2
|
|
|
|
Using the ``php.ini`` directive as the default value for ``gc_probability``
|
|
was introduced in Symfony 7.2.
|
|
|
|
.. _session-database:
|
|
|
|
Store Sessions in a Database
|
|
----------------------------
|
|
|
|
Symfony stores sessions in files by default. If your application is served by
|
|
multiple servers, you'll need to use a database instead to make sessions work
|
|
across different servers.
|
|
|
|
Symfony can store sessions in all kinds of databases (relational, NoSQL and
|
|
key-value) but recommends key-value databases like Redis to get best
|
|
performance.
|
|
|
|
Store Sessions in a key-value Database (Redis)
|
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
|
|
This section assumes that you have a fully-working Redis server and have also
|
|
installed and configured the `phpredis extension`_.
|
|
|
|
You have two different options to use Redis to store sessions:
|
|
|
|
The first PHP-based option is to configure Redis session handler directly
|
|
in the server ``php.ini`` file:
|
|
|
|
.. code-block:: ini
|
|
|
|
; php.ini
|
|
session.save_handler = redis
|
|
session.save_path = "tcp://192.168.0.178:6379?auth=REDIS_PASSWORD"
|
|
|
|
The second option is to configure Redis sessions in Symfony. First, define
|
|
a Symfony service for the connection to the Redis server:
|
|
|
|
.. configuration-block::
|
|
|
|
.. code-block:: yaml
|
|
|
|
# config/services.yaml
|
|
services:
|
|
# ...
|
|
Symfony\Component\HttpFoundation\Session\Storage\Handler\RedisSessionHandler:
|
|
arguments:
|
|
- '@Redis'
|
|
# you can optionally pass an array of options. The only options are 'prefix' and 'ttl',
|
|
# which define the prefix to use for the keys to avoid collision on the Redis server
|
|
# and the expiration time for any given entry (in seconds), defaults are 'sf_s' and null:
|
|
# - { 'prefix': 'my_prefix', 'ttl': 600 }
|
|
|
|
Redis:
|
|
# you can also use \RedisArray, \RedisCluster, \Relay\Relay or \Predis\Client classes
|
|
class: \Redis
|
|
calls:
|
|
- connect:
|
|
- '%env(REDIS_HOST)%'
|
|
- '%env(int:REDIS_PORT)%'
|
|
|
|
# uncomment the following if your Redis server requires a password
|
|
# - auth:
|
|
# - '%env(REDIS_PASSWORD)%'
|
|
|
|
# uncomment the following if your Redis server requires a user and a password (when user is not default)
|
|
# - auth:
|
|
# - ['%env(REDIS_USER)%','%env(REDIS_PASSWORD)%']
|
|
|
|
.. code-block:: 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 https://symfony.com/schema/dic/services/services-1.0.xsd">
|
|
|
|
<services>
|
|
<!-- you can also use \RedisArray, \RedisCluster, \Relay\Relay or \Predis\Client classes -->
|
|
<service id="Redis" class="Redis">
|
|
<call method="connect">
|
|
<argument>%env(REDIS_HOST)%</argument>
|
|
<argument>%env(int:REDIS_PORT)%</argument>
|
|
</call>
|
|
|
|
<!-- uncomment the following if your Redis server requires a password:
|
|
<call method="auth">
|
|
<argument>%env(REDIS_PASSWORD)%</argument>
|
|
</call> -->
|
|
|
|
<!-- uncomment the following if your Redis server requires a user and a password (when user is not default):
|
|
<call method="auth">
|
|
<argument>%env(REDIS_USER)%</argument>
|
|
<argument>%env(REDIS_PASSWORD)%</argument>
|
|
</call> -->
|
|
</service>
|
|
|
|
<service id="Symfony\Component\HttpFoundation\Session\Storage\Handler\RedisSessionHandler">
|
|
<argument type="service" id="Redis"/>
|
|
<!-- you can optionally pass an array of options. The only options are 'prefix' and 'ttl',
|
|
which define the prefix to use for the keys to avoid collision on the Redis server
|
|
and the expiration time for any given entry (in seconds), defaults are 'sf_s' and null:
|
|
<argument type="collection">
|
|
<argument key="prefix">my_prefix</argument>
|
|
<argument key="ttl">600</argument>
|
|
</argument> -->
|
|
</service>
|
|
</services>
|
|
</container>
|
|
|
|
.. code-block:: php
|
|
|
|
// config/services.php
|
|
use Symfony\Component\DependencyInjection\Reference;
|
|
use Symfony\Component\HttpFoundation\Session\Storage\Handler\RedisSessionHandler;
|
|
|
|
$container
|
|
// you can also use \RedisArray, \RedisCluster, \Relay\Relay or \Predis\Client classes
|
|
->register('Redis', \Redis::class)
|
|
->addMethodCall('connect', ['%env(REDIS_HOST)%', '%env(int:REDIS_PORT)%'])
|
|
// uncomment the following if your Redis server requires a password:
|
|
// ->addMethodCall('auth', ['%env(REDIS_PASSWORD)%'])
|
|
// uncomment the following if your Redis server requires a user and a password (when user is not default):
|
|
// ->addMethodCall('auth', ['%env(REDIS_USER)%', '%env(REDIS_PASSWORD)%'])
|
|
|
|
->register(RedisSessionHandler::class)
|
|
->addArgument(
|
|
new Reference('Redis'),
|
|
// you can optionally pass an array of options. The only options are 'prefix' and 'ttl',
|
|
// which define the prefix to use for the keys to avoid collision on the Redis server
|
|
// and the expiration time for any given entry (in seconds), defaults are 'sf_s' and null:
|
|
// ['prefix' => 'my_prefix', 'ttl' => 600],
|
|
)
|
|
;
|
|
|
|
Next, use the :ref:`handler_id <config-framework-session-handler-id>`
|
|
configuration option to tell Symfony to use this service as the session handler:
|
|
|
|
.. configuration-block::
|
|
|
|
.. code-block:: yaml
|
|
|
|
# config/packages/framework.yaml
|
|
framework:
|
|
# ...
|
|
session:
|
|
handler_id: Symfony\Component\HttpFoundation\Session\Storage\Handler\RedisSessionHandler
|
|
|
|
.. code-block:: xml
|
|
|
|
<!-- config/packages/framework.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
|
|
https://symfony.com/schema/dic/services/services-1.0.xsd
|
|
http://symfony.com/schema/dic/symfony
|
|
https://symfony.com/schema/dic/symfony/symfony-1.0.xsd">
|
|
|
|
<framework:config>
|
|
<!-- ... -->
|
|
<framework:session handler-id="Symfony\Component\HttpFoundation\Session\Storage\Handler\RedisSessionHandler"/>
|
|
</framework:config>
|
|
</container>
|
|
|
|
.. code-block:: php
|
|
|
|
// config/packages/framework.php
|
|
use Symfony\Component\HttpFoundation\Session\Storage\Handler\RedisSessionHandler;
|
|
use Symfony\Config\FrameworkConfig;
|
|
|
|
return static function (FrameworkConfig $framework): void {
|
|
// ...
|
|
$framework->session()
|
|
->handlerId(RedisSessionHandler::class)
|
|
;
|
|
};
|
|
|
|
Symfony will now use your Redis server to read and write the session data. The
|
|
main drawback of this solution is that Redis does not perform session locking,
|
|
so you can face *race conditions* when accessing sessions. For example, you may
|
|
see an *"Invalid CSRF token"* error because two requests were made in parallel
|
|
and only the first one stored the CSRF token in the session.
|
|
|
|
.. seealso::
|
|
|
|
If you use Memcached instead of Redis, follow a similar approach but
|
|
replace ``RedisSessionHandler`` by
|
|
:class:`Symfony\\Component\\HttpFoundation\\Session\\Storage\\Handler\\MemcachedSessionHandler`.
|
|
|
|
.. tip::
|
|
|
|
When using Redis with a DSN in the
|
|
:ref:`handler_id <config-framework-session-handler-id>` config option, you can
|
|
add the ``prefix`` and ``ttl`` options as query string parameters in the DSN.
|
|
|
|
.. _session-database-pdo:
|
|
|
|
Store Sessions in a Relational Database (MariaDB, MySQL, PostgreSQL)
|
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
|
|
Symfony includes a
|
|
:class:`Symfony\\Component\\HttpFoundation\\Session\\Storage\\Handler\\PdoSessionHandler`
|
|
to store sessions in relational databases like MariaDB, MySQL and PostgreSQL.
|
|
To use it, first register a new handler service with your database credentials:
|
|
|
|
.. configuration-block::
|
|
|
|
.. code-block:: yaml
|
|
|
|
# config/services.yaml
|
|
services:
|
|
# ...
|
|
|
|
Symfony\Component\HttpFoundation\Session\Storage\Handler\PdoSessionHandler:
|
|
arguments:
|
|
- '%env(DATABASE_URL)%'
|
|
|
|
# you can also use PDO configuration, but requires passing two arguments
|
|
# - 'mysql:dbname=mydatabase; host=myhost; port=myport'
|
|
# - { db_username: myuser, db_password: mypassword }
|
|
|
|
.. code-block:: xml
|
|
|
|
<!-- 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
|
|
https://symfony.com/schema/dic/services/services-1.0.xsd
|
|
https://symfony.com/schema/dic/symfony/symfony-1.0.xsd">
|
|
|
|
<services>
|
|
<service id="Symfony\Component\HttpFoundation\Session\Storage\Handler\PdoSessionHandler">
|
|
<argument>%env(DATABASE_URL)%</argument>
|
|
|
|
<!-- you can also use PDO configuration, but requires passing two arguments: -->
|
|
<!-- <argument>mysql:dbname=mydatabase; host=myhost; port=myport</argument>
|
|
<argument type="collection">
|
|
<argument key="db_username">myuser</argument>
|
|
<argument key="db_password">mypassword</argument>
|
|
</argument> -->
|
|
</service>
|
|
</services>
|
|
</container>
|
|
|
|
.. code-block:: php
|
|
|
|
// config/services.php
|
|
namespace Symfony\Component\DependencyInjection\Loader\Configurator;
|
|
|
|
use Symfony\Component\HttpFoundation\Session\Storage\Handler\PdoSessionHandler;
|
|
|
|
return static function (ContainerConfigurator $container): void {
|
|
$services = $container->services();
|
|
|
|
$services->set(PdoSessionHandler::class)
|
|
->args([
|
|
env('DATABASE_URL'),
|
|
// you can also use PDO configuration, but requires passing two arguments:
|
|
// 'mysql:dbname=mydatabase; host=myhost; port=myport',
|
|
// ['db_username' => 'myuser', 'db_password' => 'mypassword'],
|
|
])
|
|
;
|
|
};
|
|
|
|
.. tip::
|
|
|
|
When using MySQL as the database, the DSN defined in ``DATABASE_URL`` can
|
|
contain the ``charset`` and ``unix_socket`` options as query string parameters.
|
|
|
|
Next, use the :ref:`handler_id <config-framework-session-handler-id>`
|
|
configuration option to tell Symfony to use this service as the session handler:
|
|
|
|
.. configuration-block::
|
|
|
|
.. code-block:: yaml
|
|
|
|
# config/packages/framework.yaml
|
|
framework:
|
|
session:
|
|
# ...
|
|
handler_id: Symfony\Component\HttpFoundation\Session\Storage\Handler\PdoSessionHandler
|
|
|
|
.. code-block:: xml
|
|
|
|
<!-- config/packages/framework.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
|
|
https://symfony.com/schema/dic/services/services-1.0.xsd
|
|
http://symfony.com/schema/dic/symfony
|
|
https://symfony.com/schema/dic/symfony/symfony-1.0.xsd">
|
|
|
|
<framework:config>
|
|
<!-- ... -->
|
|
<framework:session
|
|
handler-id="Symfony\Component\HttpFoundation\Session\Storage\Handler\PdoSessionHandler"/>
|
|
</framework:config>
|
|
</container>
|
|
|
|
.. code-block:: php
|
|
|
|
// config/packages/framework.php
|
|
use Symfony\Component\HttpFoundation\Session\Storage\Handler\PdoSessionHandler;
|
|
use Symfony\Config\FrameworkConfig;
|
|
|
|
return static function (FrameworkConfig $framework): void {
|
|
// ...
|
|
$framework->session()
|
|
->handlerId(PdoSessionHandler::class)
|
|
;
|
|
};
|
|
|
|
Configuring the Session Table and Column Names
|
|
..............................................
|
|
|
|
The table used to store sessions is called ``sessions`` by default and defines
|
|
certain column names. You can configure these values with the second argument
|
|
passed to the ``PdoSessionHandler`` service:
|
|
|
|
.. configuration-block::
|
|
|
|
.. code-block:: yaml
|
|
|
|
# config/services.yaml
|
|
services:
|
|
# ...
|
|
|
|
Symfony\Component\HttpFoundation\Session\Storage\Handler\PdoSessionHandler:
|
|
arguments:
|
|
- '%env(DATABASE_URL)%'
|
|
- { db_table: 'customer_session', db_id_col: 'guid' }
|
|
|
|
.. code-block:: xml
|
|
|
|
<!-- 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
|
|
https://symfony.com/schema/dic/services/services-1.0.xsd">
|
|
|
|
<services>
|
|
<service id="Symfony\Component\HttpFoundation\Session\Storage\Handler\PdoSessionHandler">
|
|
<argument>%env(DATABASE_URL)%</argument>
|
|
<argument type="collection">
|
|
<argument key="db_table">customer_session</argument>
|
|
<argument key="db_id_col">guid</argument>
|
|
</argument>
|
|
</service>
|
|
</services>
|
|
</container>
|
|
|
|
.. code-block:: php
|
|
|
|
// config/services.php
|
|
namespace Symfony\Component\DependencyInjection\Loader\Configurator;
|
|
|
|
use Symfony\Component\HttpFoundation\Session\Storage\Handler\PdoSessionHandler;
|
|
|
|
return static function (ContainerConfigurator $container): void {
|
|
$services = $container->services();
|
|
|
|
$services->set(PdoSessionHandler::class)
|
|
->args([
|
|
env('DATABASE_URL'),
|
|
['db_table' => 'customer_session', 'db_id_col' => 'guid'],
|
|
])
|
|
;
|
|
};
|
|
|
|
These are parameters that you can configure:
|
|
|
|
``db_table`` (default ``sessions``):
|
|
The name of the session table in your database;
|
|
|
|
``db_username``: (default: ``''``)
|
|
The username used to connect when using the PDO configuration (when using
|
|
the connection based on the ``DATABASE_URL`` env var, it overrides the
|
|
username defined in the env var).
|
|
|
|
``db_password``: (default: ``''``)
|
|
The password used to connect when using the PDO configuration (when using
|
|
the connection based on the ``DATABASE_URL`` env var, it overrides the
|
|
password defined in the env var).
|
|
|
|
``db_id_col`` (default ``sess_id``):
|
|
The name of the column where to store the session ID (column type: ``VARCHAR(128)``);
|
|
|
|
``db_data_col`` (default ``sess_data``):
|
|
The name of the column where to store the session data (column type: ``BLOB``);
|
|
|
|
``db_time_col`` (default ``sess_time``):
|
|
The name of the column where to store the session creation timestamp (column type: ``INTEGER``);
|
|
|
|
``db_lifetime_col`` (default ``sess_lifetime``):
|
|
The name of the column where to store the session lifetime (column type: ``INTEGER``);
|
|
|
|
``db_connection_options`` (default: ``[]``)
|
|
An array of driver-specific connection options;
|
|
|
|
``lock_mode`` (default: ``LOCK_TRANSACTIONAL``)
|
|
The strategy for locking the database to avoid *race conditions*. Possible
|
|
values are ``LOCK_NONE`` (no locking), ``LOCK_ADVISORY`` (application-level
|
|
locking) and ``LOCK_TRANSACTIONAL`` (row-level locking).
|
|
|
|
Preparing the Database to Store Sessions
|
|
........................................
|
|
|
|
Before storing sessions in the database, you must create the table that stores
|
|
the information.
|
|
|
|
With Doctrine installed, the session table will be automatically generated when
|
|
you run the ``make:migration`` command if the database targeted by doctrine is
|
|
identical to the one used by this component.
|
|
|
|
Or if you prefer to create the table yourself and the table has not already been
|
|
created, the session handler provides a method called
|
|
:method:`Symfony\\Component\\HttpFoundation\\Session\\Storage\\Handler\\PdoSessionHandler::createTable`
|
|
to set up this table for you according to the database engine used::
|
|
|
|
try {
|
|
$sessionHandlerService->createTable();
|
|
} catch (\PDOException $exception) {
|
|
// the table could not be created for some reason
|
|
}
|
|
|
|
If the table already exists an exception will be thrown.
|
|
|
|
If you would rather set up the table yourself, it's recommended to generate an
|
|
empty database migration with the following command:
|
|
|
|
.. code-block:: terminal
|
|
|
|
$ php bin/console doctrine:migrations:generate
|
|
|
|
Then, find the appropriate SQL for your database below, add it to the migration
|
|
file and run the migration with the following command:
|
|
|
|
.. code-block:: terminal
|
|
|
|
$ php bin/console doctrine:migrations:migrate
|
|
|
|
If needed, you can also add this table to your schema by calling
|
|
:method:`Symfony\\Component\\HttpFoundation\\Session\\Storage\\Handler\\PdoSessionHandler::configureSchema`
|
|
method in your code.
|
|
|
|
.. _mysql:
|
|
|
|
MariaDB/MySQL
|
|
+++++++++++++
|
|
|
|
.. code-block:: sql
|
|
|
|
CREATE TABLE `sessions` (
|
|
`sess_id` VARBINARY(128) NOT NULL PRIMARY KEY,
|
|
`sess_data` BLOB NOT NULL,
|
|
`sess_lifetime` INTEGER UNSIGNED NOT NULL,
|
|
`sess_time` INTEGER UNSIGNED NOT NULL,
|
|
INDEX `sess_lifetime_idx` (`sess_lifetime`)
|
|
) COLLATE utf8mb4_bin, ENGINE = InnoDB;
|
|
|
|
.. note::
|
|
|
|
A ``BLOB`` column type (which is the one used by default by ``createTable()``)
|
|
stores up to 64 kb. If the user session data exceeds this, an exception may
|
|
be thrown or their session will be silently reset. Consider using a ``MEDIUMBLOB``
|
|
if you need more space.
|
|
|
|
PostgreSQL
|
|
++++++++++
|
|
|
|
.. code-block:: sql
|
|
|
|
CREATE TABLE sessions (
|
|
sess_id VARCHAR(128) NOT NULL PRIMARY KEY,
|
|
sess_data BYTEA NOT NULL,
|
|
sess_lifetime INTEGER NOT NULL,
|
|
sess_time INTEGER NOT NULL
|
|
);
|
|
CREATE INDEX sess_lifetime_idx ON sessions (sess_lifetime);
|
|
|
|
Microsoft SQL Server
|
|
++++++++++++++++++++
|
|
|
|
.. code-block:: sql
|
|
|
|
CREATE TABLE sessions (
|
|
sess_id VARCHAR(128) NOT NULL PRIMARY KEY,
|
|
sess_data NVARCHAR(MAX) NOT NULL,
|
|
sess_lifetime INTEGER NOT NULL,
|
|
sess_time INTEGER NOT NULL,
|
|
INDEX sess_lifetime_idx (sess_lifetime)
|
|
);
|
|
|
|
.. _session-database-mongodb:
|
|
|
|
Store Sessions in a NoSQL Database (MongoDB)
|
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
|
|
Symfony includes a
|
|
:class:`Symfony\\Component\\HttpFoundation\\Session\\Storage\\Handler\\MongoDbSessionHandler`
|
|
to store sessions in the MongoDB NoSQL database. First, make sure to have a
|
|
working MongoDB connection in your Symfony application as explained in the
|
|
`DoctrineMongoDBBundle configuration`_ article.
|
|
|
|
Then, register a new handler service for ``MongoDbSessionHandler`` and pass it
|
|
the MongoDB connection as argument, and the required parameters:
|
|
|
|
``database``:
|
|
The name of the database
|
|
|
|
``collection``:
|
|
The name of the collection
|
|
|
|
.. configuration-block::
|
|
|
|
.. code-block:: yaml
|
|
|
|
# config/services.yaml
|
|
services:
|
|
# ...
|
|
|
|
Symfony\Component\HttpFoundation\Session\Storage\Handler\MongoDbSessionHandler:
|
|
arguments:
|
|
- '@doctrine_mongodb.odm.default_connection'
|
|
- { database: '%env(MONGODB_DB)%', collection: 'sessions' }
|
|
|
|
.. code-block:: xml
|
|
|
|
<!-- 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
|
|
https://symfony.com/schema/dic/services/services-1.0.xsd
|
|
https://symfony.com/schema/dic/symfony/symfony-1.0.xsd">
|
|
|
|
<services>
|
|
<service id="Symfony\Component\HttpFoundation\Session\Storage\Handler\MongoDbSessionHandler">
|
|
<argument type="service">doctrine_mongodb.odm.default_connection</argument>
|
|
<argument type="collection">
|
|
<argument key="database">%env('MONGODB_DB')%</argument>
|
|
<argument key="collection">sessions</argument>
|
|
</argument>
|
|
</service>
|
|
</services>
|
|
</container>
|
|
|
|
.. code-block:: php
|
|
|
|
// config/services.php
|
|
namespace Symfony\Component\DependencyInjection\Loader\Configurator;
|
|
|
|
use Symfony\Component\HttpFoundation\Session\Storage\Handler\MongoDbSessionHandler;
|
|
|
|
return static function (ContainerConfigurator $container): void {
|
|
$services = $container->services();
|
|
|
|
$services->set(MongoDbSessionHandler::class)
|
|
->args([
|
|
service('doctrine_mongodb.odm.default_connection'),
|
|
['database' => '%env("MONGODB_DB")%', 'collection' => 'sessions']
|
|
])
|
|
;
|
|
};
|
|
|
|
Next, use the :ref:`handler_id <config-framework-session-handler-id>`
|
|
configuration option to tell Symfony to use this service as the session handler:
|
|
|
|
.. configuration-block::
|
|
|
|
.. code-block:: yaml
|
|
|
|
# config/packages/framework.yaml
|
|
framework:
|
|
session:
|
|
# ...
|
|
handler_id: Symfony\Component\HttpFoundation\Session\Storage\Handler\MongoDbSessionHandler
|
|
|
|
.. code-block:: xml
|
|
|
|
<!-- config/packages/framework.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
|
|
https://symfony.com/schema/dic/services/services-1.0.xsd
|
|
http://symfony.com/schema/dic/symfony
|
|
https://symfony.com/schema/dic/symfony/symfony-1.0.xsd">
|
|
|
|
<framework:config>
|
|
<!-- ... -->
|
|
<framework:session
|
|
handler-id="Symfony\Component\HttpFoundation\Session\Storage\Handler\MongoDbSessionHandler"/>
|
|
</framework:config>
|
|
</container>
|
|
|
|
.. code-block:: php
|
|
|
|
// config/packages/framework.php
|
|
use Symfony\Component\HttpFoundation\Session\Storage\Handler\MongoDbSessionHandler;
|
|
use Symfony\Config\FrameworkConfig;
|
|
|
|
return static function (FrameworkConfig $framework): void {
|
|
// ...
|
|
$framework->session()
|
|
->handlerId(MongoDbSessionHandler::class)
|
|
;
|
|
};
|
|
|
|
That's all! Symfony will now use your MongoDB server to read and write the
|
|
session data. You do not need to do anything to initialize your session
|
|
collection. However, you may want to add an index to improve garbage collection
|
|
performance. Run this from the `MongoDB shell`_:
|
|
|
|
.. code-block:: javascript
|
|
|
|
use session_db
|
|
db.session.createIndex( { "expires_at": 1 }, { expireAfterSeconds: 0 } )
|
|
|
|
Configuring the Session Field Names
|
|
...................................
|
|
|
|
The collection used to store sessions defines certain field names. You can
|
|
configure these values with the second argument passed to the
|
|
``MongoDbSessionHandler`` service:
|
|
|
|
.. configuration-block::
|
|
|
|
.. code-block:: yaml
|
|
|
|
# config/services.yaml
|
|
services:
|
|
# ...
|
|
|
|
Symfony\Component\HttpFoundation\Session\Storage\Handler\MongoDbSessionHandler:
|
|
arguments:
|
|
- '@doctrine_mongodb.odm.default_connection'
|
|
-
|
|
database: '%env(MONGODB_DB)%'
|
|
collection: 'sessions'
|
|
id_field: '_guid'
|
|
expiry_field: 'eol'
|
|
|
|
.. code-block:: xml
|
|
|
|
<!-- 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
|
|
https://symfony.com/schema/dic/services/services-1.0.xsd">
|
|
|
|
<services>
|
|
<service id="Symfony\Component\HttpFoundation\Session\Storage\Handler\MongoDbSessionHandler">
|
|
<argument type="service">doctrine_mongodb.odm.default_connection</argument>
|
|
<argument type="collection">
|
|
<argument key="database">%env('MONGODB_DB')%</argument>
|
|
<argument key="collection">sessions</argument>
|
|
<argument key="id_field">_guid</argument>
|
|
<argument key="expiry_field">eol</argument>
|
|
</argument>
|
|
</service>
|
|
</services>
|
|
</container>
|
|
|
|
.. code-block:: php
|
|
|
|
// config/services.php
|
|
namespace Symfony\Component\DependencyInjection\Loader\Configurator;
|
|
|
|
use Symfony\Component\HttpFoundation\Session\Storage\Handler\MongoDbSessionHandler;
|
|
|
|
return static function (ContainerConfigurator $container): void {
|
|
$services = $container->services();
|
|
|
|
$services->set(MongoDbSessionHandler::class)
|
|
->args([
|
|
service('doctrine_mongodb.odm.default_connection'),
|
|
[
|
|
'database' => '%env('MONGODB_DB')%',
|
|
'collection' => 'sessions'
|
|
'id_field' => '_guid',
|
|
'expiry_field' => 'eol',
|
|
],
|
|
])
|
|
;
|
|
};
|
|
|
|
These are parameters that you can configure:
|
|
|
|
``id_field`` (default ``_id``):
|
|
The name of the field where to store the session ID;
|
|
|
|
``data_field`` (default ``data``):
|
|
The name of the field where to store the session data;
|
|
|
|
``time_field`` (default ``time``):
|
|
The name of the field where to store the session creation timestamp;
|
|
|
|
``expiry_field`` (default ``expires_at``):
|
|
The name of the field where to store the session lifetime.
|
|
|
|
Migrating Between Session Handlers
|
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
|
|
If your application changes the way sessions are stored, use the
|
|
:class:`Symfony\\Component\\HttpFoundation\\Session\\Storage\\Handler\\MigratingSessionHandler`
|
|
to migrate between old and new save handlers without losing session data.
|
|
|
|
This is the recommended migration workflow:
|
|
|
|
#. Switch to the migrating handler, with your new handler as the write-only one.
|
|
The old handler behaves as usual and sessions get written to the new one::
|
|
|
|
$sessionStorage = new MigratingSessionHandler($oldSessionStorage, $newSessionStorage);
|
|
|
|
#. After your session gc period, verify that the data in the new handler is correct.
|
|
#. Update the migrating handler to use the old handler as the write-only one, so
|
|
the sessions will now be read from the new handler. This step allows easier rollbacks::
|
|
|
|
$sessionStorage = new MigratingSessionHandler($newSessionStorage, $oldSessionStorage);
|
|
|
|
#. After verifying that the sessions in your application are working, switch
|
|
from the migrating handler to the new handler.
|
|
|
|
.. _session-configure-ttl:
|
|
|
|
Configuring the Session TTL
|
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
|
|
Symfony by default will use PHP's ini setting ``session.gc_maxlifetime`` as
|
|
session lifetime. When you store sessions in a database, you can also
|
|
configure your own TTL in the framework configuration or even at runtime.
|
|
|
|
.. note::
|
|
|
|
Changing the ini setting is not possible once the session is started so
|
|
if you want to use a different TTL depending on which user is logged
|
|
in, you must do it at runtime using the callback method below.
|
|
|
|
Configure the TTL
|
|
.................
|
|
|
|
You need to pass the TTL in the options array of the session handler you are using:
|
|
|
|
.. configuration-block::
|
|
|
|
.. code-block:: yaml
|
|
|
|
# config/services.yaml
|
|
services:
|
|
# ...
|
|
Symfony\Component\HttpFoundation\Session\Storage\Handler\RedisSessionHandler:
|
|
arguments:
|
|
- '@Redis'
|
|
- { 'ttl': 600 }
|
|
|
|
.. code-block:: xml
|
|
|
|
<!-- config/services.xml -->
|
|
<services>
|
|
<service id="Symfony\Component\HttpFoundation\Session\Storage\Handler\RedisSessionHandler">
|
|
<argument type="service" id="Redis"/>
|
|
<argument type="collection">
|
|
<argument key="ttl">600</argument>
|
|
</argument>
|
|
</service>
|
|
</services>
|
|
|
|
.. code-block:: php
|
|
|
|
// config/services.php
|
|
use Symfony\Component\HttpFoundation\Session\Storage\Handler\RedisSessionHandler;
|
|
|
|
$services
|
|
->set(RedisSessionHandler::class)
|
|
->args([
|
|
service('Redis'),
|
|
['ttl' => 600],
|
|
]);
|
|
|
|
Configure the TTL Dynamically at Runtime
|
|
........................................
|
|
|
|
If you would like to have a different TTL for different users or sessions
|
|
for whatever reason, this is also possible by passing a callback as the TTL
|
|
value. The callback will be called right before the session is written and
|
|
has to return an integer which will be used as TTL.
|
|
|
|
.. configuration-block::
|
|
|
|
.. code-block:: yaml
|
|
|
|
# config/services.yaml
|
|
services:
|
|
# ...
|
|
Symfony\Component\HttpFoundation\Session\Storage\Handler\RedisSessionHandler:
|
|
arguments:
|
|
- '@Redis'
|
|
- { 'ttl': !closure '@my.ttl.handler' }
|
|
|
|
my.ttl.handler:
|
|
class: Some\InvokableClass # some class with an __invoke() method
|
|
arguments:
|
|
# Inject whatever dependencies you need to be able to resolve a TTL for the current session
|
|
- '@security'
|
|
|
|
.. code-block:: xml
|
|
|
|
<!-- config/services.xml -->
|
|
<services>
|
|
<service id="Symfony\Component\HttpFoundation\Session\Storage\Handler\RedisSessionHandler">
|
|
<argument type="service" id="Redis"/>
|
|
<argument type="collection">
|
|
<argument key="ttl" type="closure" id="my.ttl.handler"/>
|
|
</argument>
|
|
</service>
|
|
<!-- some class with an __invoke() method -->
|
|
<service id="my.ttl.handler" class="Some\InvokableClass">
|
|
<!-- Inject whatever dependencies you need to be able to resolve a TTL for the current session -->
|
|
<argument type="service" id="security"/>
|
|
</service>
|
|
</services>
|
|
|
|
.. code-block:: php
|
|
|
|
// config/services.php
|
|
use Symfony\Component\HttpFoundation\Session\Storage\Handler\RedisSessionHandler;
|
|
|
|
$services
|
|
->set(RedisSessionHandler::class)
|
|
->args([
|
|
service('Redis'),
|
|
['ttl' => closure(service('my.ttl.handler'))],
|
|
]);
|
|
|
|
$services
|
|
// some class with an __invoke() method
|
|
->set('my.ttl.handler', 'Some\InvokableClass')
|
|
// Inject whatever dependencies you need to be able to resolve a TTL for the current session
|
|
->args([service('security')]);
|
|
|
|
.. _locale-sticky-session:
|
|
|
|
Making the Locale "Sticky" during a User's Session
|
|
--------------------------------------------------
|
|
|
|
Symfony stores the locale setting in the Request, which means that this setting
|
|
is not automatically saved ("sticky") across requests. But, you *can* store the
|
|
locale in the session, so that it's used on subsequent requests.
|
|
|
|
Creating a LocaleSubscriber
|
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
|
|
Create a :ref:`new event subscriber <events-subscriber>`. Typically,
|
|
``_locale`` is used as a routing parameter to signify the locale, though you
|
|
can determine the correct locale however you want::
|
|
|
|
// src/EventSubscriber/LocaleSubscriber.php
|
|
namespace App\EventSubscriber;
|
|
|
|
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
|
|
use Symfony\Component\HttpKernel\Event\RequestEvent;
|
|
use Symfony\Component\HttpKernel\KernelEvents;
|
|
|
|
class LocaleSubscriber implements EventSubscriberInterface
|
|
{
|
|
public function __construct(
|
|
private string $defaultLocale = 'en',
|
|
) {
|
|
}
|
|
|
|
public function onKernelRequest(RequestEvent $event): void
|
|
{
|
|
$request = $event->getRequest();
|
|
if (!$request->hasPreviousSession()) {
|
|
return;
|
|
}
|
|
|
|
// try to see if the locale has been set as a _locale routing parameter
|
|
if ($locale = $request->attributes->get('_locale')) {
|
|
$request->getSession()->set('_locale', $locale);
|
|
} else {
|
|
// if no explicit locale has been set on this request, use one from the session
|
|
$request->setLocale($request->getSession()->get('_locale', $this->defaultLocale));
|
|
}
|
|
}
|
|
|
|
public static function getSubscribedEvents(): array
|
|
{
|
|
return [
|
|
// must be registered before (i.e. with a higher priority than) the default Locale listener
|
|
KernelEvents::REQUEST => [['onKernelRequest', 20]],
|
|
];
|
|
}
|
|
}
|
|
|
|
If you're using the :ref:`default services.yaml configuration
|
|
<service-container-services-load-example>`, you're done! Symfony will
|
|
automatically know about the event subscriber and call the ``onKernelRequest``
|
|
method on each request.
|
|
|
|
To see it working, either set the ``_locale`` key on the session manually (e.g.
|
|
via some "Change Locale" route & controller), or create a route with the
|
|
:ref:`_locale default <translation-locale-url>`.
|
|
|
|
.. sidebar:: Explicitly Configure the Subscriber
|
|
|
|
You can also explicitly configure it, in order to pass in the
|
|
:ref:`default_locale <config-framework-default_locale>`:
|
|
|
|
.. configuration-block::
|
|
|
|
.. code-block:: yaml
|
|
|
|
# config/services.yaml
|
|
services:
|
|
# ...
|
|
|
|
App\EventSubscriber\LocaleSubscriber:
|
|
arguments: ['%kernel.default_locale%']
|
|
# uncomment the next line if you are not using autoconfigure
|
|
# tags: [kernel.event_subscriber]
|
|
|
|
.. code-block:: xml
|
|
|
|
<!-- 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
|
|
https://symfony.com/schema/dic/services/services-1.0.xsd">
|
|
|
|
<services>
|
|
<service id="App\EventSubscriber\LocaleSubscriber">
|
|
<argument>%kernel.default_locale%</argument>
|
|
|
|
<!-- uncomment the next line if you are not using autoconfigure -->
|
|
<!-- <tag name="kernel.event_subscriber"/> -->
|
|
</service>
|
|
</services>
|
|
</container>
|
|
|
|
.. code-block:: php
|
|
|
|
// config/services.php
|
|
use App\EventSubscriber\LocaleSubscriber;
|
|
|
|
$container->register(LocaleSubscriber::class)
|
|
->addArgument('%kernel.default_locale%')
|
|
// uncomment the next line if you are not using autoconfigure
|
|
// ->addTag('kernel.event_subscriber')
|
|
;
|
|
|
|
Now celebrate by changing the user's locale and seeing that it's sticky
|
|
throughout the request.
|
|
|
|
Remember, to get the user's locale, always use the :method:`Request::getLocale
|
|
<Symfony\\Component\\HttpFoundation\\Request::getLocale>` method::
|
|
|
|
// from a controller...
|
|
use Symfony\Component\HttpFoundation\Request;
|
|
|
|
public function index(Request $request): void
|
|
{
|
|
$locale = $request->getLocale();
|
|
}
|
|
|
|
Setting the Locale Based on the User's Preferences
|
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
|
|
You might want to improve this technique even further and define the locale
|
|
based on the user entity of the logged in user. However, since the
|
|
``LocaleSubscriber`` is called before the ``FirewallListener``, which is
|
|
responsible for handling authentication and setting the user token on the
|
|
``TokenStorage``, you have no access to the user which is logged in.
|
|
|
|
Suppose you have a ``locale`` property on your ``User`` entity and want to use
|
|
this as the locale for the given user. To accomplish this, you can hook into
|
|
the login process and update the user's session with this locale value before
|
|
they are redirected to their first page.
|
|
|
|
To do this, you need an event subscriber on the ``LoginSuccessEvent::class``
|
|
event::
|
|
|
|
// src/EventSubscriber/UserLocaleSubscriber.php
|
|
namespace App\EventSubscriber;
|
|
|
|
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
|
|
use Symfony\Component\HttpFoundation\RequestStack;
|
|
use Symfony\Component\Security\Http\Event\LoginSuccessEvent;
|
|
|
|
/**
|
|
* Stores the locale of the user in the session after the
|
|
* login. This can be used by the LocaleSubscriber afterwards.
|
|
*/
|
|
class UserLocaleSubscriber implements EventSubscriberInterface
|
|
{
|
|
public function __construct(
|
|
private RequestStack $requestStack,
|
|
) {
|
|
}
|
|
|
|
public function onLoginSuccess(LoginSuccessEvent $event): void
|
|
{
|
|
$user = $event->getUser();
|
|
|
|
if (null !== $user->getLocale()) {
|
|
$this->requestStack->getSession()->set('_locale', $user->getLocale());
|
|
}
|
|
}
|
|
|
|
public static function getSubscribedEvents(): array
|
|
{
|
|
return [
|
|
LoginSuccessEvent::class => 'onLoginSuccess',
|
|
];
|
|
}
|
|
}
|
|
|
|
.. warning::
|
|
|
|
In order to update the language immediately after a user has changed their
|
|
language preferences, you also need to update the session when you change
|
|
the ``User`` entity.
|
|
|
|
Session Proxies
|
|
---------------
|
|
|
|
The session proxy mechanism has a variety of uses and this article demonstrates
|
|
two common ones. Rather than using the regular session handler, you can create
|
|
a custom save handler by defining a class that extends the
|
|
:class:`Symfony\\Component\\HttpFoundation\\Session\\Storage\\Proxy\\SessionHandlerProxy`
|
|
class.
|
|
|
|
Then, define the class as a :ref:`service
|
|
<service-container-creating-service>`. If you're using the :ref:`default
|
|
services.yaml configuration <service-container-services-load-example>`, that
|
|
happens automatically.
|
|
|
|
Finally, use the ``framework.session.handler_id`` configuration option to tell
|
|
Symfony to use your session handler instead of the default one:
|
|
|
|
.. configuration-block::
|
|
|
|
.. code-block:: yaml
|
|
|
|
# config/packages/framework.yaml
|
|
framework:
|
|
session:
|
|
# ...
|
|
handler_id: App\Session\CustomSessionHandler
|
|
|
|
.. code-block:: xml
|
|
|
|
<!-- config/packages/framework.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
|
|
https://symfony.com/schema/dic/services/services-1.0.xsd">
|
|
|
|
<framework:config>
|
|
<framework:session handler-id="App\Session\CustomSessionHandler"/>
|
|
</framework:config>
|
|
</container>
|
|
|
|
.. code-block:: php
|
|
|
|
// config/packages/framework.php
|
|
use App\Session\CustomSessionHandler;
|
|
use Symfony\Config\FrameworkConfig;
|
|
|
|
return static function (FrameworkConfig $framework): void {
|
|
// ...
|
|
$framework->session()
|
|
->handlerId(CustomSessionHandler::class)
|
|
;
|
|
};
|
|
|
|
Keep reading the next sections to learn how to use the session handlers in
|
|
practice to solve two common use cases: encrypt session information and define
|
|
read-only guest sessions.
|
|
|
|
Encryption of Session Data
|
|
~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
|
|
If you want to encrypt the session data, you can use the proxy to encrypt and
|
|
decrypt the session as required. The following example uses the `php-encryption`_
|
|
library, but you can adapt it to any other library that you may be using::
|
|
|
|
// src/Session/EncryptedSessionProxy.php
|
|
namespace App\Session;
|
|
|
|
use Defuse\Crypto\Crypto;
|
|
use Defuse\Crypto\Key;
|
|
use Symfony\Component\HttpFoundation\Session\Storage\Proxy\SessionHandlerProxy;
|
|
|
|
class EncryptedSessionProxy extends SessionHandlerProxy
|
|
{
|
|
public function __construct(
|
|
private \SessionHandlerInterface $handler,
|
|
private Key $key
|
|
) {
|
|
parent::__construct($handler);
|
|
}
|
|
|
|
public function read($id): string
|
|
{
|
|
$data = parent::read($id);
|
|
|
|
return Crypto::decrypt($data, $this->key);
|
|
}
|
|
|
|
public function write($id, $data): string
|
|
{
|
|
$data = Crypto::encrypt($data, $this->key);
|
|
|
|
return parent::write($id, $data);
|
|
}
|
|
}
|
|
|
|
Another possibility to encrypt session data is to decorate the
|
|
``session.marshaller`` service, which points out to
|
|
:class:`Symfony\\Component\\HttpFoundation\\Session\\Storage\\Handler\\MarshallingSessionHandler`.
|
|
You can decorate this handler with a marshaller that uses encryption,
|
|
like the :class:`Symfony\\Component\\Cache\\Marshaller\\SodiumMarshaller`.
|
|
|
|
First, you need to generate a secure key and add it to your :doc:`secret
|
|
store </configuration/secrets>` as ``SESSION_DECRYPTION_FILE``:
|
|
|
|
.. code-block:: terminal
|
|
|
|
$ php -r 'echo base64_encode(sodium_crypto_box_keypair());'
|
|
|
|
Then, register the ``SodiumMarshaller`` service using this key:
|
|
|
|
.. configuration-block::
|
|
|
|
.. code-block:: yaml
|
|
|
|
# config/services.yaml
|
|
services:
|
|
|
|
# ...
|
|
Symfony\Component\Cache\Marshaller\SodiumMarshaller:
|
|
decorates: 'session.marshaller'
|
|
arguments:
|
|
- ['%env(file:resolve:SESSION_DECRYPTION_FILE)%']
|
|
- '@.inner'
|
|
|
|
.. code-block:: xml
|
|
|
|
<!-- 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
|
|
https://symfony.com/schema/dic/services/services-1.0.xsd"
|
|
>
|
|
<services>
|
|
<service id="Symfony\Component\Cache\Marshaller\SodiumMarshaller" decorates="session.marshaller">
|
|
<argument type="collection">
|
|
<argument>env(file:resolve:SESSION_DECRYPTION_FILE)</argument>
|
|
</argument>
|
|
<argument type="service" id=".inner"/>
|
|
</service>
|
|
</services>
|
|
</container>
|
|
|
|
.. code-block:: php
|
|
|
|
// config/services.php
|
|
use Symfony\Component\Cache\Marshaller\SodiumMarshaller;
|
|
use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator;
|
|
// ...
|
|
|
|
return function(ContainerConfigurator $container) {
|
|
$services = $container->services();
|
|
|
|
// ...
|
|
|
|
$services->set(SodiumMarshaller::class)
|
|
->decorate('session.marshaller')
|
|
->args([
|
|
[env('file:resolve:SESSION_DECRYPTION_FILE')],
|
|
service('.inner'),
|
|
]);
|
|
};
|
|
|
|
.. danger::
|
|
|
|
This will encrypt the values of the cache items, but not the cache keys. Be
|
|
careful not to leak sensitive data in the keys.
|
|
|
|
Read-only Guest Sessions
|
|
~~~~~~~~~~~~~~~~~~~~~~~~
|
|
|
|
There are some applications where a session is required for guest users, but
|
|
where there is no particular need to persist the session. In this case you can
|
|
intercept the session before it is written::
|
|
|
|
// src/Session/ReadOnlySessionProxy.php
|
|
namespace App\Session;
|
|
|
|
use App\Entity\User;
|
|
use Symfony\Bundle\SecurityBundle\Security;
|
|
use Symfony\Component\HttpFoundation\Session\Storage\Proxy\SessionHandlerProxy;
|
|
|
|
class ReadOnlySessionProxy extends SessionHandlerProxy
|
|
{
|
|
public function __construct(
|
|
private \SessionHandlerInterface $handler,
|
|
private Security $security
|
|
) {
|
|
parent::__construct($handler);
|
|
}
|
|
|
|
public function write($id, $data): string
|
|
{
|
|
if ($this->getUser() && $this->getUser()->isGuest()) {
|
|
return;
|
|
}
|
|
|
|
return parent::write($id, $data);
|
|
}
|
|
|
|
private function getUser(): ?User
|
|
{
|
|
$user = $this->security->getUser();
|
|
if (is_object($user)) {
|
|
return $user;
|
|
}
|
|
|
|
return null;
|
|
}
|
|
}
|
|
|
|
.. _session-avoid-start:
|
|
|
|
Integrating with Legacy Applications
|
|
------------------------------------
|
|
|
|
If you're integrating the Symfony full-stack Framework into a legacy
|
|
application that starts the session with ``session_start()``, you may still be
|
|
able to use Symfony's session management by using the PHP Bridge session.
|
|
|
|
If the application has its own PHP save handler, you can specify ``null``
|
|
for the ``handler_id``:
|
|
|
|
.. configuration-block::
|
|
|
|
.. code-block:: yaml
|
|
|
|
# config/packages/framework.yaml
|
|
framework:
|
|
session:
|
|
storage_factory_id: session.storage.factory.php_bridge
|
|
handler_id: ~
|
|
|
|
.. code-block:: xml
|
|
|
|
<!-- config/packages/framework.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
|
|
https://symfony.com/schema/dic/services/services-1.0.xsd">
|
|
|
|
<framework:config>
|
|
<framework:session storage-factory-id="session.storage.factory.php_bridge"
|
|
handler-id="null"
|
|
/>
|
|
</framework:config>
|
|
</container>
|
|
|
|
.. code-block:: php
|
|
|
|
// config/packages/framework.php
|
|
use Symfony\Config\FrameworkConfig;
|
|
|
|
return static function (FrameworkConfig $framework): void {
|
|
$framework->session()
|
|
->storageFactoryId('session.storage.factory.php_bridge')
|
|
->handlerId(null)
|
|
;
|
|
};
|
|
|
|
.. code-block:: php-standalone
|
|
|
|
use Symfony\Component\HttpFoundation\Session\Session;
|
|
use Symfony\Component\HttpFoundation\Session\Storage\PhpBridgeSessionStorage;
|
|
|
|
// legacy application configures session
|
|
ini_set('session.save_handler', 'files');
|
|
ini_set('session.save_path', '/tmp');
|
|
session_start();
|
|
|
|
// Get Symfony to interface with this existing session
|
|
$session = new Session(new PhpBridgeSessionStorage());
|
|
|
|
// symfony will now interface with the existing PHP session
|
|
$session->start();
|
|
|
|
Otherwise, if the problem is that you cannot avoid the application
|
|
starting the session with ``session_start()``, you can still make use of
|
|
a Symfony based session save handler by specifying the save handler as in
|
|
the example below:
|
|
|
|
.. configuration-block::
|
|
|
|
.. code-block:: yaml
|
|
|
|
# config/packages/framework.yaml
|
|
framework:
|
|
session:
|
|
storage_factory_id: session.storage.factory.php_bridge
|
|
handler_id: session.handler.native_file
|
|
|
|
.. code-block:: xml
|
|
|
|
<!-- config/packages/framework.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
|
|
https://symfony.com/schema/dic/services/services-1.0.xsd">
|
|
|
|
<framework:config>
|
|
<framework:session storage-id="session.storage.factory.php_bridge"
|
|
handler-id="session.handler.native_file"
|
|
/>
|
|
</framework:config>
|
|
</container>
|
|
|
|
.. code-block:: php
|
|
|
|
// config/packages/framework.php
|
|
use Symfony\Config\FrameworkConfig;
|
|
|
|
return static function (FrameworkConfig $framework): void {
|
|
$framework->session()
|
|
->storageFactoryId('session.storage.factory.php_bridge')
|
|
->handlerId('session.handler.native_file')
|
|
;
|
|
};
|
|
|
|
.. note::
|
|
|
|
If the legacy application requires its own session save handler, do not
|
|
override this. Instead set ``handler_id: ~``. Note that a save handler
|
|
cannot be changed once the session has been started. If the application
|
|
starts the session before Symfony is initialized, the save handler will
|
|
have already been set. In this case, you will need ``handler_id: ~``.
|
|
Only override the save handler if you are sure the legacy application
|
|
can use the Symfony save handler without side effects and that the session
|
|
has not been started before Symfony is initialized.
|
|
|
|
.. _`Twig Live Component`: https://symfony.com/bundles/ux-live-component/current/index.html
|
|
.. _`phpredis extension`: https://github.com/phpredis/phpredis
|
|
.. _`DoctrineMongoDBBundle configuration`: https://symfony.com/doc/master/bundles/DoctrineMongoDBBundle/config.html
|
|
.. _`MongoDB shell`: https://docs.mongodb.com/manual/mongo/
|
|
.. _`php-encryption`: https://github.com/defuse/php-encryption
|