Deprecate session.storage

This commit is contained in:
Jérémy Derussé 2021-01-31 20:45:01 +01:00
parent c757845643
commit 37c591516a
No known key found for this signature in database
GPG Key ID: 2083FA5758C473D2
54 changed files with 468 additions and 39 deletions

View File

@ -31,6 +31,8 @@ Form
FrameworkBundle FrameworkBundle
--------------- ---------------
* Deprecate the `session.storage` alias and `session.storage.*` services, use the `session.storage.factory` alias and `session.storage.factory.*` services instead
* Deprecate the `framework.session.storage_id` configuration option, use the `framework.session.storage_factory_id` configuration option instead
* Deprecate the `session` service and the `SessionInterface` alias, use the `\Symfony\Component\HttpFoundation\Request::getSession()` or the new `\Symfony\Component\HttpFoundation\RequestStack::getSession()` methods instead * Deprecate the `session` service and the `SessionInterface` alias, use the `\Symfony\Component\HttpFoundation\Request::getSession()` or the new `\Symfony\Component\HttpFoundation\RequestStack::getSession()` methods instead
HttpFoundation HttpFoundation

View File

@ -69,6 +69,8 @@ Form
FrameworkBundle FrameworkBundle
--------------- ---------------
* Remove the `session.storage` alias and `session.storage.*` services, use the `session.storage.factory` alias and `session.storage.factory.*` services instead
* Remove `framework.session.storage_id` configuration option, use the `framework.session.storage_factory_id` configuration option instead
* Remove the `session` service and the `SessionInterface` alias, use the `\Symfony\Component\HttpFoundation\Request::getSession()` or the new `\Symfony\Component\HttpFoundation\RequestStack::getSession()` methods instead * Remove the `session` service and the `SessionInterface` alias, use the `\Symfony\Component\HttpFoundation\Request::getSession()` or the new `\Symfony\Component\HttpFoundation\RequestStack::getSession()` methods instead
* `MicroKernelTrait::configureRoutes()` is now always called with a `RoutingConfigurator` * `MicroKernelTrait::configureRoutes()` is now always called with a `RoutingConfigurator`
* The "framework.router.utf8" configuration option defaults to `true` * The "framework.router.utf8" configuration option defaults to `true`

View File

@ -4,6 +4,8 @@ CHANGELOG
5.3 5.3
--- ---
* Deprecate the `session.storage` alias and `session.storage.*` services, use the `session.storage.factory` alias and `session.storage.factory.*` services instead
* Deprecate the `framework.session.storage_id` configuration option, use the `framework.session.storage_factory_id` configuration option instead
* Deprecate the `session` service and the `SessionInterface` alias, use the `Request::getSession()` or the new `RequestStack::getSession()` methods instead * Deprecate the `session` service and the `SessionInterface` alias, use the `Request::getSession()` or the new `RequestStack::getSession()` methods instead
* Added `AbstractController::renderForm()` to render a form and set the appropriate HTTP status code * Added `AbstractController::renderForm()` to render a form and set the appropriate HTTP status code
* Added support for configuring PHP error level to log levels * Added support for configuring PHP error level to log levels

View File

@ -22,7 +22,7 @@ class SessionPass implements CompilerPassInterface
{ {
public function process(ContainerBuilder $container) public function process(ContainerBuilder $container)
{ {
if (!$container->has('session.storage')) { if (!$container->has('session.factory')) {
return; return;
} }

View File

@ -600,8 +600,15 @@ class Configuration implements ConfigurationInterface
->arrayNode('session') ->arrayNode('session')
->info('session configuration') ->info('session configuration')
->canBeEnabled() ->canBeEnabled()
->beforeNormalization()
->ifTrue(function ($v) {
return \is_array($v) && isset($v['storage_id']) && isset($v['storage_factory_id']);
})
->thenInvalid('You cannot use both "storage_id" and "storage_factory_id" at the same time under "framework.session"')
->end()
->children() ->children()
->scalarNode('storage_id')->defaultValue('session.storage.native')->end() ->scalarNode('storage_id')->defaultValue('session.storage.native')->end()
->scalarNode('storage_factory_id')->defaultNull()->end()
->scalarNode('handler_id')->defaultValue('session.handler.native_file')->end() ->scalarNode('handler_id')->defaultValue('session.handler.native_file')->end()
->scalarNode('name') ->scalarNode('name')
->validate() ->validate()

View File

@ -70,6 +70,7 @@ use Symfony\Component\HttpClient\Retry\GenericRetryStrategy;
use Symfony\Component\HttpClient\RetryableHttpClient; use Symfony\Component\HttpClient\RetryableHttpClient;
use Symfony\Component\HttpClient\ScopingHttpClient; use Symfony\Component\HttpClient\ScopingHttpClient;
use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Session\Storage\SessionStorageInterface;
use Symfony\Component\HttpKernel\CacheClearer\CacheClearerInterface; use Symfony\Component\HttpKernel\CacheClearer\CacheClearerInterface;
use Symfony\Component\HttpKernel\CacheWarmer\CacheWarmerInterface; use Symfony\Component\HttpKernel\CacheWarmer\CacheWarmerInterface;
use Symfony\Component\HttpKernel\Controller\ArgumentValueResolverInterface; use Symfony\Component\HttpKernel\Controller\ArgumentValueResolverInterface;
@ -1028,7 +1029,20 @@ class FrameworkExtension extends Extension
$loader->load('session.php'); $loader->load('session.php');
// session storage // session storage
if (null === $config['storage_factory_id']) {
trigger_deprecation('symfony/framework-bundle', '5.3', 'Not setting the "framework.session.storage_factory_id" configuration option is deprecated, it will default to "session.storage.factory.native" and will replace the "framework.session.storage_id" configuration option in version 6.0.');
$container->setAlias('session.storage', $config['storage_id']); $container->setAlias('session.storage', $config['storage_id']);
$container->setAlias('session.storage.factory', 'session.storage.factory.service');
} else {
$container->setAlias('session.storage.factory', $config['storage_factory_id']);
$container->removeAlias(SessionStorageInterface::class);
$container->removeDefinition('session.storage.metadata_bag');
$container->removeDefinition('session.storage.native');
$container->removeDefinition('session.storage.php_bridge');
$container->removeAlias('session.storage.filesystem');
}
$options = ['cache_limiter' => '0']; $options = ['cache_limiter' => '0'];
foreach (['name', 'cookie_lifetime', 'cookie_path', 'cookie_domain', 'cookie_secure', 'cookie_httponly', 'cookie_samesite', 'use_cookies', 'gc_maxlifetime', 'gc_probability', 'gc_divisor', 'sid_length', 'sid_bits_per_character'] as $key) { foreach (['name', 'cookie_lifetime', 'cookie_path', 'cookie_domain', 'cookie_secure', 'cookie_httponly', 'cookie_samesite', 'use_cookies', 'gc_maxlifetime', 'gc_probability', 'gc_divisor', 'sid_length', 'sid_bits_per_character'] as $key) {
if (isset($config[$key])) { if (isset($config[$key])) {
@ -1037,11 +1051,16 @@ class FrameworkExtension extends Extension
} }
if ('auto' === ($options['cookie_secure'] ?? null)) { if ('auto' === ($options['cookie_secure'] ?? null)) {
if (null === $config['storage_factory_id']) {
$locator = $container->getDefinition('session_listener')->getArgument(0); $locator = $container->getDefinition('session_listener')->getArgument(0);
$locator->setValues($locator->getValues() + [ $locator->setValues($locator->getValues() + [
'session_storage' => new Reference('session.storage', ContainerInterface::IGNORE_ON_INVALID_REFERENCE), 'session_storage' => new Reference('session.storage', ContainerInterface::IGNORE_ON_INVALID_REFERENCE),
'request_stack' => new Reference('request_stack'), 'request_stack' => new Reference('request_stack'),
]); ]);
} else {
$container->getDefinition('session.storage.factory.native')->replaceArgument(3, true);
$container->getDefinition('session.storage.factory.php_bridge')->replaceArgument(2, true);
}
} }
$container->setParameter('session.storage.options', $options); $container->setParameter('session.storage.options', $options);
@ -1049,8 +1068,14 @@ class FrameworkExtension extends Extension
// session handler (the internal callback registered with PHP session management) // session handler (the internal callback registered with PHP session management)
if (null === $config['handler_id']) { if (null === $config['handler_id']) {
// Set the handler class to be null // Set the handler class to be null
if ($container->hasDefinition('session.storage.native')) {
$container->getDefinition('session.storage.native')->replaceArgument(1, null); $container->getDefinition('session.storage.native')->replaceArgument(1, null);
$container->getDefinition('session.storage.php_bridge')->replaceArgument(0, null); $container->getDefinition('session.storage.php_bridge')->replaceArgument(0, null);
} else {
$container->getDefinition('session.storage.factory.native')->replaceArgument(1, null);
$container->getDefinition('session.storage.factory.php_bridge')->replaceArgument(0, null);
}
$container->setAlias('session.handler', 'session.handler.native_file'); $container->setAlias('session.handler', 'session.handler.native_file');
} else { } else {
$container->resolveEnvPlaceholders($config['handler_id'], null, $usedEnvs); $container->resolveEnvPlaceholders($config['handler_id'], null, $usedEnvs);

View File

@ -18,7 +18,6 @@ use Symfony\Component\BrowserKit\History;
use Symfony\Component\DependencyInjection\ContainerInterface; use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpFoundation\Session\Session;
use Symfony\Component\HttpKernel\HttpKernelBrowser; use Symfony\Component\HttpKernel\HttpKernelBrowser;
use Symfony\Component\HttpKernel\KernelInterface; use Symfony\Component\HttpKernel\KernelInterface;
use Symfony\Component\HttpKernel\Profiler\Profile as HttpProfile; use Symfony\Component\HttpKernel\Profiler\Profile as HttpProfile;
@ -123,7 +122,7 @@ class KernelBrowser extends HttpKernelBrowser
$token = new TestBrowserToken($user->getRoles(), $user); $token = new TestBrowserToken($user->getRoles(), $user);
$token->setAuthenticated(true); $token->setAuthenticated(true);
$session = new Session($this->getContainer()->get('test.service_container')->get('session.storage')); $session = $this->getContainer()->get('test.service_container')->get('session.factory')->createSession();
$session->set('_security_'.$firewallContext, serialize($token)); $session->set('_security_'.$firewallContext, serialize($token));
$session->save(); $session->save();

View File

@ -107,6 +107,7 @@
<xsd:complexType name="session"> <xsd:complexType name="session">
<xsd:attribute name="enabled" type="xsd:boolean" /> <xsd:attribute name="enabled" type="xsd:boolean" />
<xsd:attribute name="storage-factory-id" type="xsd:string" />
<xsd:attribute name="storage-id" type="xsd:string" /> <xsd:attribute name="storage-id" type="xsd:string" />
<xsd:attribute name="handler-id" type="xsd:string" /> <xsd:attribute name="handler-id" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" /> <xsd:attribute name="name" type="xsd:string" />

View File

@ -16,6 +16,7 @@ use Symfony\Component\HttpFoundation\Session\Attribute\AttributeBag;
use Symfony\Component\HttpFoundation\Session\Flash\FlashBag; use Symfony\Component\HttpFoundation\Session\Flash\FlashBag;
use Symfony\Component\HttpFoundation\Session\Flash\FlashBagInterface; use Symfony\Component\HttpFoundation\Session\Flash\FlashBagInterface;
use Symfony\Component\HttpFoundation\Session\Session; use Symfony\Component\HttpFoundation\Session\Session;
use Symfony\Component\HttpFoundation\Session\SessionFactory;
use Symfony\Component\HttpFoundation\Session\SessionInterface; use Symfony\Component\HttpFoundation\Session\SessionInterface;
use Symfony\Component\HttpFoundation\Session\Storage\Handler\AbstractSessionHandler; use Symfony\Component\HttpFoundation\Session\Storage\Handler\AbstractSessionHandler;
use Symfony\Component\HttpFoundation\Session\Storage\Handler\IdentityMarshaller; use Symfony\Component\HttpFoundation\Session\Storage\Handler\IdentityMarshaller;
@ -25,8 +26,12 @@ use Symfony\Component\HttpFoundation\Session\Storage\Handler\SessionHandlerFacto
use Symfony\Component\HttpFoundation\Session\Storage\Handler\StrictSessionHandler; use Symfony\Component\HttpFoundation\Session\Storage\Handler\StrictSessionHandler;
use Symfony\Component\HttpFoundation\Session\Storage\MetadataBag; use Symfony\Component\HttpFoundation\Session\Storage\MetadataBag;
use Symfony\Component\HttpFoundation\Session\Storage\MockFileSessionStorage; use Symfony\Component\HttpFoundation\Session\Storage\MockFileSessionStorage;
use Symfony\Component\HttpFoundation\Session\Storage\MockFileSessionStorageFactory;
use Symfony\Component\HttpFoundation\Session\Storage\NativeSessionStorage; use Symfony\Component\HttpFoundation\Session\Storage\NativeSessionStorage;
use Symfony\Component\HttpFoundation\Session\Storage\NativeSessionStorageFactory;
use Symfony\Component\HttpFoundation\Session\Storage\PhpBridgeSessionStorage; use Symfony\Component\HttpFoundation\Session\Storage\PhpBridgeSessionStorage;
use Symfony\Component\HttpFoundation\Session\Storage\PhpBridgeSessionStorageFactory;
use Symfony\Component\HttpFoundation\Session\Storage\ServiceSessionFactory;
use Symfony\Component\HttpFoundation\Session\Storage\SessionStorageInterface; use Symfony\Component\HttpFoundation\Session\Storage\SessionStorageInterface;
use Symfony\Component\HttpKernel\EventListener\SessionListener; use Symfony\Component\HttpKernel\EventListener\SessionListener;
@ -35,17 +40,57 @@ return static function (ContainerConfigurator $container) {
$container->services() $container->services()
->set('.session.do-not-use', Session::class) // to be removed in 6.0 ->set('.session.do-not-use', Session::class) // to be removed in 6.0
->factory([service('session.factory'), 'createSession'])
->set('session.factory', SessionFactory::class)
->args([ ->args([
service('session.storage'), service('request_stack'),
null, // AttributeBagInterface service('session.storage.factory'),
null, // FlashBagInterface
[service('session_listener'), 'onSessionUsage'], [service('session_listener'), 'onSessionUsage'],
]) ])
->set('session.storage.factory.native', NativeSessionStorageFactory::class)
->args([
param('session.storage.options'),
service('session.handler'),
inline_service(MetadataBag::class)
->args([
param('session.metadata.storage_key'),
param('session.metadata.update_threshold'),
]),
false,
])
->set('session.storage.factory.php_bridge', PhpBridgeSessionStorageFactory::class)
->args([
service('session.handler'),
inline_service(MetadataBag::class)
->args([
param('session.metadata.storage_key'),
param('session.metadata.update_threshold'),
]),
false,
])
->set('session.storage.factory.mock_file', MockFileSessionStorageFactory::class)
->args([
param('kernel.cache_dir').'/sessions',
'MOCKSESSID',
inline_service(MetadataBag::class)
->args([
param('session.metadata.storage_key'),
param('session.metadata.update_threshold'),
]),
])
->set('session.storage.factory.service', ServiceSessionFactory::class)
->args([
service('session.storage'),
])
->deprecate('symfony/framework-bundle', '5.3', 'The "%service_id%" service is deprecated, use "session.storage.factory.native", "session.storage.factory.php_bridge" or "session.storage.factory.mock_file" instead.')
->set('.session.deprecated', SessionInterface::class) // to be removed in 6.0 ->set('.session.deprecated', SessionInterface::class) // to be removed in 6.0
->factory([inline_service(DeprecatedSessionFactory::class)->args([service('request_stack')]), 'getSession']) ->factory([inline_service(DeprecatedSessionFactory::class)->args([service('request_stack')]), 'getSession'])
->alias(SessionInterface::class, '.session.do-not-use') ->alias(SessionInterface::class, '.session.do-not-use')
->deprecate('symfony/framework-bundle', '5.3', 'The "%alias_id%" alias is deprecated, use "$requestStack->getSession()" instead.') ->deprecate('symfony/framework-bundle', '5.3', 'The "%alias_id%" alias is deprecated, use "$requestStack->getSession()" instead.')
->alias(SessionStorageInterface::class, 'session.storage') ->alias(SessionStorageInterface::class, 'session.storage')
->deprecate('symfony/framework-bundle', '5.3', 'The "%alias_id%" alias is deprecated, use "session.storage.factory" instead.')
->alias(\SessionHandlerInterface::class, 'session.handler') ->alias(\SessionHandlerInterface::class, 'session.handler')
->set('session.storage.metadata_bag', MetadataBag::class) ->set('session.storage.metadata_bag', MetadataBag::class)
@ -53,6 +98,7 @@ return static function (ContainerConfigurator $container) {
param('session.metadata.storage_key'), param('session.metadata.storage_key'),
param('session.metadata.update_threshold'), param('session.metadata.update_threshold'),
]) ])
->deprecate('symfony/framework-bundle', '5.3', 'The "%service_id%" service is deprecated, create your own "session.storage.factory" instead.')
->set('session.storage.native', NativeSessionStorage::class) ->set('session.storage.native', NativeSessionStorage::class)
->args([ ->args([
@ -60,12 +106,14 @@ return static function (ContainerConfigurator $container) {
service('session.handler'), service('session.handler'),
service('session.storage.metadata_bag'), service('session.storage.metadata_bag'),
]) ])
->deprecate('symfony/framework-bundle', '5.3', 'The "%service_id%" service is deprecated, use "session.storage.factory.native" instead.')
->set('session.storage.php_bridge', PhpBridgeSessionStorage::class) ->set('session.storage.php_bridge', PhpBridgeSessionStorage::class)
->args([ ->args([
service('session.handler'), service('session.handler'),
service('session.storage.metadata_bag'), service('session.storage.metadata_bag'),
]) ])
->deprecate('symfony/framework-bundle', '5.3', 'The "%service_id%" service is deprecated, use "session.storage.factory.php_bridge" instead.')
->set('session.flash_bag', FlashBag::class) ->set('session.flash_bag', FlashBag::class)
->factory([service('.session.do-not-use'), 'getFlashBag']) ->factory([service('.session.do-not-use'), 'getFlashBag'])
@ -83,6 +131,7 @@ return static function (ContainerConfigurator $container) {
'MOCKSESSID', 'MOCKSESSID',
service('session.storage.metadata_bag'), service('session.storage.metadata_bag'),
]) ])
->deprecate('symfony/framework-bundle', '5.3', 'The "%service_id%" service is deprecated, use "session.storage.factory.mock_file" instead.')
->set('session.handler.native_file', StrictSessionHandler::class) ->set('session.handler.native_file', StrictSessionHandler::class)
->args([ ->args([
@ -108,6 +157,7 @@ return static function (ContainerConfigurator $container) {
// for BC // for BC
->alias('session.storage.filesystem', 'session.storage.mock_file') ->alias('session.storage.filesystem', 'session.storage.mock_file')
->deprecate('symfony/framework-bundle', '5.3', 'The "%alias_id%" alias is deprecated, use "session.storage.factory.mock_file" instead.')
->set('session.marshaller', IdentityMarshaller::class) ->set('session.marshaller', IdentityMarshaller::class)

View File

@ -22,7 +22,7 @@ class SessionPassTest extends TestCase
{ {
$container = new ContainerBuilder(); $container = new ContainerBuilder();
$container $container
->register('session.storage'); // marker service ->register('session.factory'); // marker service
$container $container
->register('.session.do-not-use'); ->register('.session.do-not-use');
@ -41,7 +41,7 @@ class SessionPassTest extends TestCase
]; ];
$container = new ContainerBuilder(); $container = new ContainerBuilder();
$container $container
->register('session.storage'); // marker service ->register('session.factory'); // marker service
$container $container
->register('session') ->register('session')
->setArguments($arguments); ->setArguments($arguments);
@ -70,7 +70,7 @@ class SessionPassTest extends TestCase
]; ];
$container = new ContainerBuilder(); $container = new ContainerBuilder();
$container $container
->register('session.storage'); // marker service ->register('session.factory'); // marker service
$container $container
->register('trueSession') ->register('trueSession')
->setArguments($arguments); ->setArguments($arguments);

View File

@ -463,6 +463,7 @@ class ConfigurationTest extends TestCase
'session' => [ 'session' => [
'enabled' => false, 'enabled' => false,
'storage_id' => 'session.storage.native', 'storage_id' => 'session.storage.native',
'storage_factory_id' => null,
'handler_id' => 'session.handler.native_file', 'handler_id' => 'session.handler.native_file',
'cookie_httponly' => true, 'cookie_httponly' => true,
'cookie_samesite' => null, 'cookie_samesite' => null,

View File

@ -6,6 +6,7 @@ $container->loadFromExtension('framework', [
'legacy_error_messages' => false, 'legacy_error_messages' => false,
], ],
'session' => [ 'session' => [
'storage_factory_id' => 'session.storage.factory.native',
'handler_id' => null, 'handler_id' => null,
], ],
]); ]);

View File

@ -27,7 +27,7 @@ $container->loadFromExtension('framework', [
'utf8' => true, 'utf8' => true,
], ],
'session' => [ 'session' => [
'storage_id' => 'session.storage.native', 'storage_factory_id' => 'session.storage.factory.native',
'handler_id' => 'session.handler.native_file', 'handler_id' => 'session.handler.native_file',
'name' => '_SYMFONY', 'name' => '_SYMFONY',
'cookie_lifetime' => 86400, 'cookie_lifetime' => 86400,

View File

@ -2,6 +2,7 @@
$container->loadFromExtension('framework', [ $container->loadFromExtension('framework', [
'session' => [ 'session' => [
'storage_factory_id' => 'session.storage.factory.native',
'handler_id' => null, 'handler_id' => null,
], ],
]); ]);

View File

@ -2,6 +2,7 @@
$container->loadFromExtension('framework', [ $container->loadFromExtension('framework', [
'session' => [ 'session' => [
'storage_factory_id' => 'session.storage.factory.native',
'handler_id' => null, 'handler_id' => null,
'cookie_secure' => 'auto', 'cookie_secure' => 'auto',
], ],

View File

@ -0,0 +1,9 @@
<?php
// To be removed in Symfony 6.0
$container->loadFromExtension('framework', [
'session' => [
'handler_id' => null,
'cookie_secure' => 'auto',
],
]);

View File

@ -0,0 +1,8 @@
<?php
// To be removed in Symfony 6.0
$container->loadFromExtension('framework', [
'session' => [
'handler_id' => null,
],
]);

View File

@ -9,6 +9,6 @@
<framework:config> <framework:config>
<framework:csrf-protection /> <framework:csrf-protection />
<framework:form legacy-error-messages="false" /> <framework:form legacy-error-messages="false" />
<framework:session /> <framework:session storage-factory-id="session.storage.factory.native" />
</framework:config> </framework:config>
</container> </container>

View File

@ -8,7 +8,7 @@
<framework:config> <framework:config>
<framework:csrf-protection field-name="_custom" /> <framework:csrf-protection field-name="_custom" />
<framework:session /> <framework:session storage-factory-id="session.storage.factory.native" />
<framework:form legacy-error-messages="false" /> <framework:form legacy-error-messages="false" />
</framework:config> </framework:config>
</container> </container>

View File

@ -9,6 +9,6 @@
<framework:config> <framework:config>
<framework:csrf-protection field-name="_custom_form" /> <framework:csrf-protection field-name="_custom_form" />
<framework:form legacy-error-messages="false" /> <framework:form legacy-error-messages="false" />
<framework:session /> <framework:session storage-factory-id="session.storage.factory.native" />
</framework:config> </framework:config>
</container> </container>

View File

@ -15,7 +15,7 @@
<framework:ssi enabled="true" /> <framework:ssi enabled="true" />
<framework:profiler only-exceptions="true" enabled="false" /> <framework:profiler only-exceptions="true" enabled="false" />
<framework:router resource="%kernel.project_dir%/config/routing.xml" type="xml" utf8="true" /> <framework:router resource="%kernel.project_dir%/config/routing.xml" type="xml" utf8="true" />
<framework:session gc-maxlifetime="90000" gc-probability="1" gc-divisor="108" storage-id="session.storage.native" handler-id="session.handler.native_file" name="_SYMFONY" cookie-lifetime="86400" cookie-path="/" cookie-domain="example.com" cookie-secure="true" cookie-httponly="false" use-cookies="true" save-path="/path/to/sessions" sid-length="22" sid-bits-per-character="4" /> <framework:session gc-maxlifetime="90000" gc-probability="1" gc-divisor="108" storage-factory-id="session.storage.factory.native" handler-id="session.handler.native_file" name="_SYMFONY" cookie-lifetime="86400" cookie-path="/" cookie-domain="example.com" cookie-secure="true" cookie-httponly="false" use-cookies="true" save-path="/path/to/sessions" sid-length="22" sid-bits-per-character="4" />
<framework:request> <framework:request>
<framework:format name="csv"> <framework:format name="csv">
<framework:mime-type>text/csv</framework:mime-type> <framework:mime-type>text/csv</framework:mime-type>

View File

@ -7,6 +7,6 @@
http://symfony.com/schema/dic/symfony https://symfony.com/schema/dic/symfony/symfony-1.0.xsd"> http://symfony.com/schema/dic/symfony https://symfony.com/schema/dic/symfony/symfony-1.0.xsd">
<framework:config> <framework:config>
<framework:session handler-id="null"/> <framework:session storage-factory-id="session.storage.factory.native" handler-id="null"/>
</framework:config> </framework:config>
</container> </container>

View File

@ -7,6 +7,6 @@
http://symfony.com/schema/dic/symfony https://symfony.com/schema/dic/symfony/symfony-1.0.xsd"> http://symfony.com/schema/dic/symfony https://symfony.com/schema/dic/symfony/symfony-1.0.xsd">
<framework:config> <framework:config>
<framework:session handler-id="null" cookie-secure="auto" /> <framework:session storage-factory-id="session.storage.factory.native" handler-id="null" cookie-secure="auto" />
</framework:config> </framework:config>
</container> </container>

View File

@ -0,0 +1,12 @@
<?xml version="1.0" ?>
<!-- To be removed in Symfony 6.0 -->
<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="null" cookie-secure="auto" />
</framework:config>
</container>

View File

@ -0,0 +1,12 @@
<?xml version="1.0" ?>
<!-- To be removed in Symfony 6.0 -->
<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="null"/>
</framework:config>
</container>

View File

@ -3,4 +3,5 @@ framework:
csrf_protection: ~ csrf_protection: ~
form: form:
legacy_error_messages: false legacy_error_messages: false
session: ~ session:
storage_factory_id: session.storage.factory.native

View File

@ -19,7 +19,7 @@ framework:
type: xml type: xml
utf8: true utf8: true
session: session:
storage_id: session.storage.native storage_factory_id: session.storage.factory.native
handler_id: session.handler.native_file handler_id: session.handler.native_file
name: _SYMFONY name: _SYMFONY
cookie_lifetime: 86400 cookie_lifetime: 86400

View File

@ -1,3 +1,4 @@
framework: framework:
session: session:
storage_factory_id: session.storage.factory.native
handler_id: null handler_id: null

View File

@ -1,4 +1,5 @@
framework: framework:
session: session:
storage_factory_id: session.storage.factory.native
handler_id: ~ handler_id: ~
cookie_secure: auto cookie_secure: auto

View File

@ -0,0 +1,5 @@
# to be removed in Symfony 6.0
framework:
session:
handler_id: ~
cookie_secure: auto

View File

@ -0,0 +1,4 @@
# to be removed in Symfony 6.0
framework:
session:
handler_id: null

View File

@ -544,7 +544,10 @@ abstract class FrameworkExtensionTest extends TestCase
$this->assertTrue($container->hasAlias(SessionInterface::class), '->registerSessionConfiguration() loads session.xml'); $this->assertTrue($container->hasAlias(SessionInterface::class), '->registerSessionConfiguration() loads session.xml');
$this->assertEquals('fr', $container->getParameter('kernel.default_locale')); $this->assertEquals('fr', $container->getParameter('kernel.default_locale'));
$this->assertEquals('session.storage.native', (string) $container->getAlias('session.storage')); $this->assertEquals('session.storage.factory.native', (string) $container->getAlias('session.storage.factory'));
$this->assertFalse($container->has('session.storage'));
$this->assertFalse($container->has('session.storage.native'));
$this->assertFalse($container->has('session.storage.php_bridge'));
$this->assertEquals('session.handler.native_file', (string) $container->getAlias('session.handler')); $this->assertEquals('session.handler.native_file', (string) $container->getAlias('session.handler'));
$options = $container->getParameter('session.storage.options'); $options = $container->getParameter('session.storage.options');
@ -568,6 +571,25 @@ abstract class FrameworkExtensionTest extends TestCase
{ {
$container = $this->createContainerFromFile('session'); $container = $this->createContainerFromFile('session');
$this->assertTrue($container->hasAlias(SessionInterface::class), '->registerSessionConfiguration() loads session.xml');
$this->assertNull($container->getDefinition('session.storage.factory.native')->getArgument(1));
$this->assertNull($container->getDefinition('session.storage.factory.php_bridge')->getArgument(0));
$this->assertSame('session.handler.native_file', (string) $container->getAlias('session.handler'));
$expected = ['session', 'initialized_session', 'logger', 'session_collector'];
$this->assertEquals($expected, array_keys($container->getDefinition('session_listener')->getArgument(0)->getValues()));
$this->assertSame(false, $container->getDefinition('session.storage.factory.native')->getArgument(3));
}
/**
* @group legacy
*/
public function testNullSessionHandlerLegacy()
{
$this->expectDeprecation('Since symfony/framework-bundle 5.3: Not setting the "framework.session.storage_factory_id" configuration option is deprecated, it will default to "session.storage.factory.native" and will replace the "framework.session.storage_id" configuration option in version 6.0.');
$container = $this->createContainerFromFile('session_legacy');
$this->assertTrue($container->hasAlias(SessionInterface::class), '->registerSessionConfiguration() loads session.xml'); $this->assertTrue($container->hasAlias(SessionInterface::class), '->registerSessionConfiguration() loads session.xml');
$this->assertNull($container->getDefinition('session.storage.native')->getArgument(1)); $this->assertNull($container->getDefinition('session.storage.native')->getArgument(1));
$this->assertNull($container->getDefinition('session.storage.php_bridge')->getArgument(0)); $this->assertNull($container->getDefinition('session.storage.php_bridge')->getArgument(0));
@ -575,6 +597,7 @@ abstract class FrameworkExtensionTest extends TestCase
$expected = ['session', 'initialized_session', 'logger', 'session_collector']; $expected = ['session', 'initialized_session', 'logger', 'session_collector'];
$this->assertEquals($expected, array_keys($container->getDefinition('session_listener')->getArgument(0)->getValues())); $this->assertEquals($expected, array_keys($container->getDefinition('session_listener')->getArgument(0)->getValues()));
$this->assertSame(false, $container->getDefinition('session.storage.factory.native')->getArgument(3));
} }
public function testRequest() public function testRequest()
@ -1479,6 +1502,19 @@ abstract class FrameworkExtensionTest extends TestCase
{ {
$container = $this->createContainerFromFile('session_cookie_secure_auto'); $container = $this->createContainerFromFile('session_cookie_secure_auto');
$expected = ['session', 'initialized_session', 'logger', 'session_collector'];
$this->assertEquals($expected, array_keys($container->getDefinition('session_listener')->getArgument(0)->getValues()));
}
/**
* @group legacy
*/
public function testSessionCookieSecureAutoLegacy()
{
$this->expectDeprecation('Since symfony/framework-bundle 5.3: Not setting the "framework.session.storage_factory_id" configuration option is deprecated, it will default to "session.storage.factory.native" and will replace the "framework.session.storage_id" configuration option in version 6.0.');
$container = $this->createContainerFromFile('session_cookie_secure_auto_legacy');
$expected = ['session', 'initialized_session', 'logger', 'session_collector', 'session_storage', 'request_stack']; $expected = ['session', 'initialized_session', 'logger', 'session_collector', 'session_storage', 'request_stack'];
$this->assertEquals($expected, array_keys($container->getDefinition('session_listener')->getArgument(0)->getValues())); $this->assertEquals($expected, array_keys($container->getDefinition('session_listener')->getArgument(0)->getValues()));
} }

View File

@ -5,6 +5,7 @@ framework:
secret: '%secret%' secret: '%secret%'
default_locale: '%env(LOCALE)%' default_locale: '%env(LOCALE)%'
session: session:
storage_factory_id: session.storage.factory.native
cookie_httponly: '%env(bool:COOKIE_HTTPONLY)%' cookie_httponly: '%env(bool:COOKIE_HTTPONLY)%'
parameters: parameters:

View File

@ -7,7 +7,8 @@ framework:
fragments: true fragments: true
profiler: true profiler: true
router: true router: true
session: true session:
storage_factory_id: session.storage.factory.native
request: true request: true
assets: true assets: true
translator: true translator: true

View File

@ -9,7 +9,7 @@ framework:
test: true test: true
default_locale: en default_locale: en
session: session:
storage_id: session.storage.mock_file storage_factory_id: session.storage.factory.mock_file
services: services:
logger: { class: Psr\Log\NullLogger } logger: { class: Psr\Log\NullLogger }

View File

@ -41,7 +41,7 @@ class RegisterTokenUsageTrackingPass implements CompilerPassInterface
TokenStorageInterface::class => new BoundArgument(new Reference('security.untracked_token_storage'), false), TokenStorageInterface::class => new BoundArgument(new Reference('security.untracked_token_storage'), false),
]); ]);
if (!$container->has('session.storage')) { if (!$container->has('session.factory') && !$container->has('session.storage')) {
$container->setAlias('security.token_storage', 'security.untracked_token_storage')->setPublic(true); $container->setAlias('security.token_storage', 'security.untracked_token_storage')->setPublic(true);
$container->getDefinition('security.untracked_token_storage')->addTag('kernel.reset', ['method' => 'reset']); $container->getDefinition('security.untracked_token_storage')->addTag('kernel.reset', ['method' => 'reset']);
} elseif ($container->hasDefinition('security.context_listener')) { } elseif ($container->hasDefinition('security.context_listener')) {

View File

@ -18,7 +18,9 @@ use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\ContainerInterface; use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\DependencyInjection\Reference; use Symfony\Component\DependencyInjection\Reference;
use Symfony\Component\HttpFoundation\Session\Session; use Symfony\Component\HttpFoundation\Session\Session;
use Symfony\Component\HttpFoundation\Session\SessionFactory;
use Symfony\Component\HttpFoundation\Session\Storage\NativeSessionStorage; use Symfony\Component\HttpFoundation\Session\Storage\NativeSessionStorage;
use Symfony\Component\HttpFoundation\Session\Storage\NativeSessionStorageFactory;
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorage; use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorage;
use Symfony\Component\Security\Core\Authentication\Token\Storage\UsageTrackingTokenStorage; use Symfony\Component\Security\Core\Authentication\Token\Storage\UsageTrackingTokenStorage;
use Symfony\Component\Security\Http\Firewall\ContextListener; use Symfony\Component\Security\Http\Firewall\ContextListener;
@ -66,7 +68,7 @@ class RegisterTokenUsageTrackingPassTest extends TestCase
$container = new ContainerBuilder(); $container = new ContainerBuilder();
$container->setParameter('security.token_storage.class', UsageTrackingTokenStorage::class); $container->setParameter('security.token_storage.class', UsageTrackingTokenStorage::class);
$container->register('session.storage', NativeSessionStorage::class); $container->register('session.factory', SessionFactory::class);
$container->register('security.context_listener', ContextListener::class) $container->register('security.context_listener', ContextListener::class)
->setArguments([ ->setArguments([
new Reference('security.untracked_token_storage'), new Reference('security.untracked_token_storage'),

View File

@ -398,6 +398,7 @@ class SecurityExtensionTest extends TestCase
], ],
[ [
[ [
'storage_factory_id' => 'session.storage.factory.native',
'cookie_secure' => true, 'cookie_secure' => true,
'cookie_samesite' => 'lax', 'cookie_samesite' => 'lax',
'save_path' => null, 'save_path' => null,

View File

@ -7,7 +7,7 @@ framework:
test: ~ test: ~
default_locale: en default_locale: en
session: session:
storage_id: session.storage.mock_file storage_factory_id: session.storage.factory.mock_file
profiler: { only_exceptions: false } profiler: { only_exceptions: false }
services: services:

View File

@ -5,7 +5,7 @@ framework:
default_locale: en default_locale: en
profiler: false profiler: false
session: session:
storage_id: session.storage.mock_file storage_factory_id: session.storage.factory.mock_file
services: services:
logger: { class: Psr\Log\NullLogger } logger: { class: Psr\Log\NullLogger }

View File

@ -9,7 +9,7 @@ framework:
test: ~ test: ~
default_locale: en default_locale: en
session: session:
storage_id: session.storage.mock_file storage_factory_id: session.storage.factory.mock_file
profiler: { only_exceptions: false } profiler: { only_exceptions: false }
services: services:

View File

@ -5,7 +5,7 @@ framework:
default_locale: en default_locale: en
profiler: false profiler: false
session: session:
storage_id: session.storage.mock_file storage_factory_id: session.storage.factory.mock_file
services: services:
logger: { class: Psr\Log\NullLogger } logger: { class: Psr\Log\NullLogger }

View File

@ -3,6 +3,7 @@ imports:
framework: framework:
session: session:
storage_factory_id: session.storage.factory.mock_file
cookie_secure: auto cookie_secure: auto
cookie_samesite: lax cookie_samesite: lax

View File

@ -10,7 +10,7 @@ framework:
test: ~ test: ~
default_locale: en default_locale: en
session: session:
storage_id: session.storage.mock_file storage_factory_id: session.storage.factory.mock_file
profiler: { only_exceptions: false } profiler: { only_exceptions: false }
services: services:

View File

@ -39,7 +39,7 @@
"symfony/dom-crawler": "^4.4|^5.0", "symfony/dom-crawler": "^4.4|^5.0",
"symfony/expression-language": "^4.4|^5.0", "symfony/expression-language": "^4.4|^5.0",
"symfony/form": "^4.4|^5.0", "symfony/form": "^4.4|^5.0",
"symfony/framework-bundle": "^5.2", "symfony/framework-bundle": "^5.3",
"symfony/process": "^4.4|^5.0", "symfony/process": "^4.4|^5.0",
"symfony/rate-limiter": "^5.2", "symfony/rate-limiter": "^5.2",
"symfony/serializer": "^4.4|^5.0", "symfony/serializer": "^4.4|^5.0",

View File

@ -43,7 +43,7 @@ class WebProfilerBundleKernel extends Kernel
$containerBuilder->loadFromExtension('framework', [ $containerBuilder->loadFromExtension('framework', [
'secret' => 'foo-secret', 'secret' => 'foo-secret',
'profiler' => ['only_exceptions' => false], 'profiler' => ['only_exceptions' => false],
'session' => ['storage_id' => 'session.storage.mock_file'], 'session' => ['storage_factory_id' => 'session.storage.factory.mock_file'],
'router' => ['utf8' => true], 'router' => ['utf8' => true],
]); ]);

View File

@ -18,7 +18,7 @@
"require": { "require": {
"php": ">=7.2.5", "php": ">=7.2.5",
"symfony/config": "^4.4|^5.0", "symfony/config": "^4.4|^5.0",
"symfony/framework-bundle": "^5.1", "symfony/framework-bundle": "^5.3",
"symfony/http-kernel": "^5.2", "symfony/http-kernel": "^5.2",
"symfony/routing": "^4.4|^5.0", "symfony/routing": "^4.4|^5.0",
"symfony/twig-bundle": "^4.4|^5.0", "symfony/twig-bundle": "^4.4|^5.0",

View File

@ -4,6 +4,7 @@ CHANGELOG
5.3 5.3
--- ---
* Add the `SessionFactory`, `NativeSessionStorageFactory`, `PhpBridgeSessionStorageFactory` and `MockFileSessionStorageFactory` classes
* Calling `Request::getSession()` when there is no available session throws a `SessionNotFoundException` * Calling `Request::getSession()` when there is no available session throws a `SessionNotFoundException`
* Add the `RequestStack::getSession` method * Add the `RequestStack::getSession` method
* Deprecate the `NamespacedAttributeBag` class * Deprecate the `NamespacedAttributeBag` class

View File

@ -0,0 +1,40 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\HttpFoundation\Session;
use Symfony\Component\HttpFoundation\RequestStack;
use Symfony\Component\HttpFoundation\Session\Storage\SessionStorageFactoryInterface;
// Help opcache.preload discover always-needed symbols
class_exists(Session::class);
/**
* @author Jérémy Derussé <jeremy@derusse.com>
*/
class SessionFactory
{
private $requestStack;
private $storageFactory;
private $usageReporter;
public function __construct(RequestStack $requestStack, SessionStorageFactoryInterface $storageFactory, callable $usageReporter = null)
{
$this->requestStack = $requestStack;
$this->storageFactory = $storageFactory;
$this->usageReporter = $usageReporter;
}
public function createSession(): SessionInterface
{
return new Session($this->storageFactory->createStorage($this->requestStack->getMasterRequest()), null, null, $this->usageReporter);
}
}

View File

@ -0,0 +1,42 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\HttpFoundation\Session\Storage;
use Symfony\Component\HttpFoundation\Request;
// Help opcache.preload discover always-needed symbols
class_exists(MockFileSessionStorage::class);
/**
* @author Jérémy Derussé <jeremy@derusse.com>
*/
class MockFileSessionStorageFactory implements SessionStorageFactoryInterface
{
private $savePath;
private $name;
private $metaBag;
/**
* @see MockFileSessionStorage constructor.
*/
public function __construct(string $savePath = null, string $name = 'MOCKSESSID', MetadataBag $metaBag = null)
{
$this->savePath = $savePath;
$this->name = $name;
$this->metaBag = $metaBag;
}
public function createStorage(?Request $request): SessionStorageInterface
{
return new MockFileSessionStorage($this->savePath, $this->name, $this->metaBag);
}
}

View File

@ -0,0 +1,49 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\HttpFoundation\Session\Storage;
use Symfony\Component\HttpFoundation\Request;
// Help opcache.preload discover always-needed symbols
class_exists(NativeSessionStorage::class);
/**
* @author Jérémy Derussé <jeremy@derusse.com>
*/
class NativeSessionStorageFactory implements SessionStorageFactoryInterface
{
private $options;
private $handler;
private $metaBag;
private $secure;
/**
* @see NativeSessionStorage constructor.
*/
public function __construct(array $options = [], $handler = null, MetadataBag $metaBag = null, bool $secure = false)
{
$this->options = $options;
$this->handler = $handler;
$this->metaBag = $metaBag;
$this->secure = $secure;
}
public function createStorage(?Request $request): SessionStorageInterface
{
$storage = new NativeSessionStorage($this->options, $this->handler, $this->metaBag);
if ($this->secure && $request && $request->isSecure()) {
$storage->setOptions(['cookie_secure' => true]);
}
return $storage;
}
}

View File

@ -0,0 +1,47 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\HttpFoundation\Session\Storage;
use Symfony\Component\HttpFoundation\Request;
// Help opcache.preload discover always-needed symbols
class_exists(PhpBridgeSessionStorage::class);
/**
* @author Jérémy Derussé <jeremy@derusse.com>
*/
class PhpBridgeSessionStorageFactory implements SessionStorageFactoryInterface
{
private $handler;
private $metaBag;
private $secure;
/**
* @see PhpBridgeSessionStorage constructor.
*/
public function __construct($handler = null, MetadataBag $metaBag = null, bool $secure = false)
{
$this->handler = $handler;
$this->metaBag = $metaBag;
$this->secure = $secure;
}
public function createStorage(?Request $request): SessionStorageInterface
{
$storage = new PhpBridgeSessionStorage($this->handler, $this->metaBag);
if ($this->secure && $request && $request->isSecure()) {
$storage->setOptions(['cookie_secure' => true]);
}
return $storage;
}
}

View File

@ -0,0 +1,38 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\HttpFoundation\Session\Storage;
use Symfony\Component\HttpFoundation\Request;
/**
* @author Jérémy Derussé <jeremy@derusse.com>
*
* @internal to be removed in Symfony 6
*/
final class ServiceSessionFactory implements SessionStorageFactoryInterface
{
private $storage;
public function __construct(SessionStorageInterface $storage)
{
$this->storage = $storage;
}
public function createStorage(?Request $request): SessionStorageInterface
{
if ($this->storage instanceof NativeSessionStorage && $request && $request->isSecure()) {
$this->storage->setOptions(['cookie_secure' => true]);
}
return $this->storage;
}
}

View File

@ -0,0 +1,25 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\HttpFoundation\Session\Storage;
use Symfony\Component\HttpFoundation\Request;
/**
* @author Jérémy Derussé <jeremy@derusse.com>
*/
interface SessionStorageFactoryInterface
{
/**
* Creates a new instance of SessionStorageInterface
*/
public function createStorage(?Request $request): SessionStorageInterface;
}