[HttpFoundation][FrameworkBundle] allow configuring the session handler with a DSN
This commit is contained in:
parent
d08273236c
commit
de9c61f423
@ -18,6 +18,7 @@ CHANGELOG
|
|||||||
* Added sort option for `translation:update` command.
|
* Added sort option for `translation:update` command.
|
||||||
* [BC Break] The `framework.messenger.routing.senders` config key is not deep merged anymore.
|
* [BC Break] The `framework.messenger.routing.senders` config key is not deep merged anymore.
|
||||||
* Added `secrets:*` commands and `%env(secret:...)%` processor to deal with secrets seamlessly.
|
* Added `secrets:*` commands and `%env(secret:...)%` processor to deal with secrets seamlessly.
|
||||||
|
* Made `framework.session.handler_id` accept a DSN
|
||||||
|
|
||||||
4.3.0
|
4.3.0
|
||||||
-----
|
-----
|
||||||
|
@ -240,6 +240,9 @@ class FrameworkExtension extends Extension
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// register cache before session so both can share the connection services
|
||||||
|
$this->registerCacheConfiguration($config['cache'], $container);
|
||||||
|
|
||||||
if ($this->isConfigEnabled($container, $config['session'])) {
|
if ($this->isConfigEnabled($container, $config['session'])) {
|
||||||
if (!\extension_loaded('session')) {
|
if (!\extension_loaded('session')) {
|
||||||
throw new LogicException('Session support cannot be enabled as the session extension is not installed. See https://php.net/session.installation for instructions.');
|
throw new LogicException('Session support cannot be enabled as the session extension is not installed. See https://php.net/session.installation for instructions.');
|
||||||
@ -326,7 +329,6 @@ class FrameworkExtension extends Extension
|
|||||||
$this->registerFragmentsConfiguration($config['fragments'], $container, $loader);
|
$this->registerFragmentsConfiguration($config['fragments'], $container, $loader);
|
||||||
$this->registerTranslatorConfiguration($config['translator'], $container, $loader, $config['default_locale']);
|
$this->registerTranslatorConfiguration($config['translator'], $container, $loader, $config['default_locale']);
|
||||||
$this->registerProfilerConfiguration($config['profiler'], $container, $loader);
|
$this->registerProfilerConfiguration($config['profiler'], $container, $loader);
|
||||||
$this->registerCacheConfiguration($config['cache'], $container);
|
|
||||||
$this->registerWorkflowConfiguration($config['workflows'], $container, $loader);
|
$this->registerWorkflowConfiguration($config['workflows'], $container, $loader);
|
||||||
$this->registerDebugConfiguration($config['php_errors'], $container, $loader);
|
$this->registerDebugConfiguration($config['php_errors'], $container, $loader);
|
||||||
$this->registerRouterConfiguration($config['router'], $container, $loader);
|
$this->registerRouterConfiguration($config['router'], $container, $loader);
|
||||||
@ -924,9 +926,20 @@ class FrameworkExtension extends Extension
|
|||||||
// Set the handler class to be null
|
// Set the handler class to be null
|
||||||
$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->resolveEnvPlaceholders($config['handler_id'], null, $usedEnvs);
|
||||||
|
|
||||||
|
if ($usedEnvs || preg_match('#^[a-z]++://#', $config['handler_id'])) {
|
||||||
|
$id = '.cache_connection.'.ContainerBuilder::hash($config['handler_id']);
|
||||||
|
|
||||||
|
$container->getDefinition('session.abstract_handler')
|
||||||
|
->replaceArgument(0, $container->hasDefinition($id) ? new Reference($id) : $config['handler_id']);
|
||||||
|
|
||||||
|
$container->setAlias('session.handler', 'session.abstract_handler')->setPrivate(true);
|
||||||
} else {
|
} else {
|
||||||
$container->setAlias('session.handler', $config['handler_id'])->setPrivate(true);
|
$container->setAlias('session.handler', $config['handler_id'])->setPrivate(true);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
$container->setParameter('session.save_path', $config['save_path']);
|
$container->setParameter('session.save_path', $config['save_path']);
|
||||||
|
|
||||||
|
@ -56,6 +56,11 @@
|
|||||||
</argument>
|
</argument>
|
||||||
</service>
|
</service>
|
||||||
|
|
||||||
|
<service id="session.abstract_handler" class="Symfony\Component\HttpFoundation\Session\Storage\Handler\AbstractSessionHandler">
|
||||||
|
<factory class="Symfony\Component\HttpFoundation\Session\Storage\Handler\SessionHandlerFactory" method="createHandler" />
|
||||||
|
<argument />
|
||||||
|
</service>
|
||||||
|
|
||||||
<service id="session_listener" class="Symfony\Component\HttpKernel\EventListener\SessionListener">
|
<service id="session_listener" class="Symfony\Component\HttpKernel\EventListener\SessionListener">
|
||||||
<tag name="kernel.event_subscriber" />
|
<tag name="kernel.event_subscriber" />
|
||||||
<argument type="service_locator">
|
<argument type="service_locator">
|
||||||
|
@ -22,7 +22,7 @@
|
|||||||
"symfony/config": "^4.3.4|^5.0",
|
"symfony/config": "^4.3.4|^5.0",
|
||||||
"symfony/dependency-injection": "^4.4|^5.0",
|
"symfony/dependency-injection": "^4.4|^5.0",
|
||||||
"symfony/error-renderer": "^4.4|^5.0",
|
"symfony/error-renderer": "^4.4|^5.0",
|
||||||
"symfony/http-foundation": "^4.3|^5.0",
|
"symfony/http-foundation": "^4.4|^5.0",
|
||||||
"symfony/http-kernel": "^4.4",
|
"symfony/http-kernel": "^4.4",
|
||||||
"symfony/polyfill-mbstring": "~1.0",
|
"symfony/polyfill-mbstring": "~1.0",
|
||||||
"symfony/filesystem": "^3.4|^4.0|^5.0",
|
"symfony/filesystem": "^3.4|^4.0|^5.0",
|
||||||
|
@ -10,6 +10,7 @@ CHANGELOG
|
|||||||
* `PdoSessionHandler` now precalculates the expiry timestamp in the lifetime column,
|
* `PdoSessionHandler` now precalculates the expiry timestamp in the lifetime column,
|
||||||
make sure to run `CREATE INDEX EXPIRY ON sessions (sess_lifetime)` to update your database
|
make sure to run `CREATE INDEX EXPIRY ON sessions (sess_lifetime)` to update your database
|
||||||
to speed up garbage collection of expired sessions.
|
to speed up garbage collection of expired sessions.
|
||||||
|
* added `SessionHandlerFactory` to create session handlers with a DSN
|
||||||
|
|
||||||
4.3.0
|
4.3.0
|
||||||
-----
|
-----
|
||||||
|
@ -0,0 +1,84 @@
|
|||||||
|
<?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\Handler;
|
||||||
|
|
||||||
|
use Doctrine\DBAL\DriverManager;
|
||||||
|
use Symfony\Component\Cache\Adapter\AbstractAdapter;
|
||||||
|
use Symfony\Component\Cache\Traits\RedisClusterProxy;
|
||||||
|
use Symfony\Component\Cache\Traits\RedisProxy;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Nicolas Grekas <p@tchwork.com>
|
||||||
|
*/
|
||||||
|
class SessionHandlerFactory
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @param \Redis|\RedisArray|\RedisCluster|\Predis\ClientInterface|RedisProxy|RedisClusterProxy|\Memcached|\PDO|string $connection Connection or DSN
|
||||||
|
*/
|
||||||
|
public static function createHandler($connection): AbstractSessionHandler
|
||||||
|
{
|
||||||
|
if (!\is_string($connection) && !\is_object($connection)) {
|
||||||
|
throw new \TypeError(sprintf('Argument 1 passed to %s() must be a string or a connection object, %s given.', __METHOD__, \gettype($connection)));
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (true) {
|
||||||
|
case $connection instanceof \Redis:
|
||||||
|
case $connection instanceof \RedisArray:
|
||||||
|
case $connection instanceof \RedisCluster:
|
||||||
|
case $connection instanceof \Predis\ClientInterface:
|
||||||
|
case $connection instanceof RedisProxy:
|
||||||
|
case $connection instanceof RedisClusterProxy:
|
||||||
|
return new RedisSessionHandler($connection);
|
||||||
|
|
||||||
|
case $connection instanceof \Memcached:
|
||||||
|
return new MemcachedSessionHandler($connection);
|
||||||
|
|
||||||
|
case $connection instanceof \PDO:
|
||||||
|
return new PdoSessionHandler($connection);
|
||||||
|
|
||||||
|
case !\is_string($connection):
|
||||||
|
throw new \InvalidArgumentException(sprintf('Unsupported Connection: %s.', \get_class($connection)));
|
||||||
|
case 0 === strpos($connection, 'file://'):
|
||||||
|
return new StrictSessionHandler(new NativeFileSessionHandler(substr($connection, 7)));
|
||||||
|
|
||||||
|
case 0 === strpos($connection, 'redis://'):
|
||||||
|
case 0 === strpos($connection, 'rediss://'):
|
||||||
|
case 0 === strpos($connection, 'memcached://'):
|
||||||
|
if (!class_exists(AbstractAdapter::class)) {
|
||||||
|
throw new InvalidArgumentException(sprintf('Unsupported DSN "%s". Try running "composer require symfony/cache".', $this->dsn));
|
||||||
|
}
|
||||||
|
$connection = AbstractAdapter::createConnection($connection, ['lazy' => true]);
|
||||||
|
|
||||||
|
return 0 === strpos($connection, 'memcached://') ? new MemcachedSessionHandler($connection) : new RedisSessionHandler($connection);
|
||||||
|
|
||||||
|
case 0 === strpos($connection, 'pdo_oci://'):
|
||||||
|
if (!class_exists(DriverManager::class)) {
|
||||||
|
throw new \InvalidArgumentException(sprintf('Unsupported DSN "%s". Try running "composer require doctrine/dbal".', $connection));
|
||||||
|
}
|
||||||
|
$connection = DriverManager::getConnection(['url' => $connection])->getWrappedConnection();
|
||||||
|
// no break;
|
||||||
|
|
||||||
|
case 0 === strpos($connection, 'mssql://'):
|
||||||
|
case 0 === strpos($connection, 'mysql://'):
|
||||||
|
case 0 === strpos($connection, 'mysql2://'):
|
||||||
|
case 0 === strpos($connection, 'pgsql://'):
|
||||||
|
case 0 === strpos($connection, 'postgres://'):
|
||||||
|
case 0 === strpos($connection, 'postgresql://'):
|
||||||
|
case 0 === strpos($connection, 'sqlsrv://'):
|
||||||
|
case 0 === strpos($connection, 'sqlite://'):
|
||||||
|
case 0 === strpos($connection, 'sqlite3://'):
|
||||||
|
return new PdoSessionHandler($connection);
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new \InvalidArgumentException(sprintf('Unsupported Connection: %s.', $connection));
|
||||||
|
}
|
||||||
|
}
|
@ -11,6 +11,7 @@
|
|||||||
|
|
||||||
namespace Symfony\Component\Lock\Store;
|
namespace Symfony\Component\Lock\Store;
|
||||||
|
|
||||||
|
use Doctrine\DBAL\Connection;
|
||||||
use Symfony\Component\Cache\Adapter\AbstractAdapter;
|
use Symfony\Component\Cache\Adapter\AbstractAdapter;
|
||||||
use Symfony\Component\Cache\Traits\RedisClusterProxy;
|
use Symfony\Component\Cache\Traits\RedisClusterProxy;
|
||||||
use Symfony\Component\Cache\Traits\RedisProxy;
|
use Symfony\Component\Cache\Traits\RedisProxy;
|
||||||
@ -25,59 +26,74 @@ use Symfony\Component\Lock\PersistingStoreInterface;
|
|||||||
class StoreFactory
|
class StoreFactory
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* @param \Redis|\RedisArray|\RedisCluster|\Predis\ClientInterface|\Memcached|\Zookeeper|string $connection Connection or DSN or Store short name
|
* @param \Redis|\RedisArray|\RedisCluster|\Predis\ClientInterface|RedisProxy|RedisClusterProxy|\Memcached|\PDO|Connection|\Zookeeper|string $connection Connection or DSN or Store short name
|
||||||
*
|
*
|
||||||
* @return PersistingStoreInterface
|
* @return PersistingStoreInterface
|
||||||
*/
|
*/
|
||||||
public static function createStore($connection)
|
public static function createStore($connection)
|
||||||
{
|
{
|
||||||
if (
|
if (!\is_string($connection) && !\is_object($connection)) {
|
||||||
$connection instanceof \Redis ||
|
throw new \TypeError(sprintf('Argument 1 passed to %s() must be a string or a connection object, %s given.', __METHOD__, \gettype($connection)));
|
||||||
$connection instanceof \RedisArray ||
|
|
||||||
$connection instanceof \RedisCluster ||
|
|
||||||
$connection instanceof \Predis\ClientInterface ||
|
|
||||||
$connection instanceof RedisProxy ||
|
|
||||||
$connection instanceof RedisClusterProxy
|
|
||||||
) {
|
|
||||||
return new RedisStore($connection);
|
|
||||||
}
|
|
||||||
if ($connection instanceof \Memcached) {
|
|
||||||
return new MemcachedStore($connection);
|
|
||||||
}
|
|
||||||
if ($connection instanceof \Zookeeper) {
|
|
||||||
return new ZookeeperStore($connection);
|
|
||||||
}
|
|
||||||
if (!\is_string($connection)) {
|
|
||||||
throw new InvalidArgumentException(sprintf('Unsupported Connection: %s.', \get_class($connection)));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (true) {
|
switch (true) {
|
||||||
|
case $connection instanceof \Redis:
|
||||||
|
case $connection instanceof \RedisArray:
|
||||||
|
case $connection instanceof \RedisCluster:
|
||||||
|
case $connection instanceof \Predis\ClientInterface:
|
||||||
|
case $connection instanceof RedisProxy:
|
||||||
|
case $connection instanceof RedisClusterProxy:
|
||||||
|
return new RedisStore($connection);
|
||||||
|
|
||||||
|
case $connection instanceof \Memcached:
|
||||||
|
return new MemcachedStore($connection);
|
||||||
|
|
||||||
|
case $connection instanceof \PDO:
|
||||||
|
case $connection instanceof Connection:
|
||||||
|
return new PdoStore($connection);
|
||||||
|
|
||||||
|
case $connection instanceof \Zookeeper:
|
||||||
|
return new ZookeeperStore($connection);
|
||||||
|
|
||||||
|
case !\is_string($connection):
|
||||||
|
throw new InvalidArgumentException(sprintf('Unsupported Connection: %s.', \get_class($connection)));
|
||||||
case 'flock' === $connection:
|
case 'flock' === $connection:
|
||||||
return new FlockStore();
|
return new FlockStore();
|
||||||
|
|
||||||
case 0 === strpos($connection, 'flock://'):
|
case 0 === strpos($connection, 'flock://'):
|
||||||
return new FlockStore(substr($connection, 8));
|
return new FlockStore(substr($connection, 8));
|
||||||
|
|
||||||
case 'semaphore' === $connection:
|
case 'semaphore' === $connection:
|
||||||
return new SemaphoreStore();
|
return new SemaphoreStore();
|
||||||
case 0 === strpos($connection, 'redis://') && class_exists(AbstractAdapter::class):
|
|
||||||
case 0 === strpos($connection, 'rediss://') && class_exists(AbstractAdapter::class):
|
case 0 === strpos($connection, 'redis://'):
|
||||||
return new RedisStore(AbstractAdapter::createConnection($connection, ['lazy' => true]));
|
case 0 === strpos($connection, 'rediss://'):
|
||||||
case 0 === strpos($connection, 'memcached://') && class_exists(AbstractAdapter::class):
|
case 0 === strpos($connection, 'memcached://'):
|
||||||
return new MemcachedStore(AbstractAdapter::createConnection($connection, ['lazy' => true]));
|
if (!class_exists(AbstractAdapter::class)) {
|
||||||
case 0 === strpos($connection, 'sqlite:'):
|
throw new InvalidArgumentException(sprintf('Unsupported DSN "%s". Try running "composer require symfony/cache".', $this->dsn));
|
||||||
|
}
|
||||||
|
$connection = AbstractAdapter::createConnection($connection, ['lazy' => true]);
|
||||||
|
|
||||||
|
return 0 === strpos($connection, 'memcached://') ? new MemcachedStore($connection) : new RedisStore($connection);
|
||||||
|
|
||||||
|
case 0 === strpos($connection, 'mssql://'):
|
||||||
case 0 === strpos($connection, 'mysql:'):
|
case 0 === strpos($connection, 'mysql:'):
|
||||||
case 0 === strpos($connection, 'pgsql:'):
|
|
||||||
case 0 === strpos($connection, 'oci:'):
|
|
||||||
case 0 === strpos($connection, 'sqlsrv:'):
|
|
||||||
case 0 === strpos($connection, 'sqlite3://'):
|
|
||||||
case 0 === strpos($connection, 'mysql2://'):
|
case 0 === strpos($connection, 'mysql2://'):
|
||||||
|
case 0 === strpos($connection, 'oci:'):
|
||||||
|
case 0 === strpos($connection, 'oci8://'):
|
||||||
|
case 0 === strpos($connection, 'pdo_oci://'):
|
||||||
|
case 0 === strpos($connection, 'pgsql:'):
|
||||||
case 0 === strpos($connection, 'postgres://'):
|
case 0 === strpos($connection, 'postgres://'):
|
||||||
case 0 === strpos($connection, 'postgresql://'):
|
case 0 === strpos($connection, 'postgresql://'):
|
||||||
case 0 === strpos($connection, 'mssql://'):
|
case 0 === strpos($connection, 'sqlsrv:'):
|
||||||
|
case 0 === strpos($connection, 'sqlite:'):
|
||||||
|
case 0 === strpos($connection, 'sqlite3://'):
|
||||||
return new PdoStore($connection);
|
return new PdoStore($connection);
|
||||||
|
|
||||||
case 0 === strpos($connection, 'zookeeper://'):
|
case 0 === strpos($connection, 'zookeeper://'):
|
||||||
return new ZookeeperStore(ZookeeperStore::createConnection($connection));
|
return new ZookeeperStore(ZookeeperStore::createConnection($connection));
|
||||||
default:
|
}
|
||||||
|
|
||||||
throw new InvalidArgumentException(sprintf('Unsupported Connection: %s.', $connection));
|
throw new InvalidArgumentException(sprintf('Unsupported Connection: %s.', $connection));
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user