Skip to content

Commit 016cfab

Browse files
[HttpFoundation][FrameworkBundle] allow configuring the session handler with a DSN
1 parent d082732 commit 016cfab

File tree

8 files changed

+151
-31
lines changed

8 files changed

+151
-31
lines changed

src/Symfony/Bundle/FrameworkBundle/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ CHANGELOG
1818
* Added sort option for `translation:update` command.
1919
* [BC Break] The `framework.messenger.routing.senders` config key is not deep merged anymore.
2020
* Added `secrets:*` commands and `%env(secret:...)%` processor to deal with secrets seamlessly.
21+
* Made `framework.session.handler_id` accept a DSN
2122

2223
4.3.0
2324
-----

src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -240,6 +240,9 @@ public function load(array $configs, ContainerBuilder $container)
240240
}
241241
}
242242

243+
// register cache before session so both can share the connection services
244+
$this->registerCacheConfiguration($config['cache'], $container);
245+
243246
if ($this->isConfigEnabled($container, $config['session'])) {
244247
if (!\extension_loaded('session')) {
245248
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 @@ public function load(array $configs, ContainerBuilder $container)
326329
$this->registerFragmentsConfiguration($config['fragments'], $container, $loader);
327330
$this->registerTranslatorConfiguration($config['translator'], $container, $loader, $config['default_locale']);
328331
$this->registerProfilerConfiguration($config['profiler'], $container, $loader);
329-
$this->registerCacheConfiguration($config['cache'], $container);
330332
$this->registerWorkflowConfiguration($config['workflows'], $container, $loader);
331333
$this->registerDebugConfiguration($config['php_errors'], $container, $loader);
332334
$this->registerRouterConfiguration($config['router'], $container, $loader);
@@ -925,7 +927,18 @@ private function registerSessionConfiguration(array $config, ContainerBuilder $c
925927
$container->getDefinition('session.storage.native')->replaceArgument(1, null);
926928
$container->getDefinition('session.storage.php_bridge')->replaceArgument(0, null);
927929
} else {
928-
$container->setAlias('session.handler', $config['handler_id'])->setPrivate(true);
930+
$container->resolveEnvPlaceholders($config['handler_id'], null, $usedEnvs);
931+
932+
if ($usedEnvs || preg_match('#^[a-z]++://#', $config['handler_id'])) {
933+
$id = '.cache_connection.'.ContainerBuilder::hash($config['handler_id']);
934+
935+
$container->getDefinition('session.abstract_handler')
936+
->replaceArgument(0, $container->hasDefinition($id) ? new Reference($id) : $config['handler_id']);
937+
938+
$container->setAlias('session.handler', 'session.abstract_handler')->setPrivate(true);
939+
} else {
940+
$container->setAlias('session.handler', $config['handler_id'])->setPrivate(true);
941+
}
929942
}
930943

931944
$container->setParameter('session.save_path', $config['save_path']);

src/Symfony/Bundle/FrameworkBundle/Resources/config/session.xml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,11 @@
5656
</argument>
5757
</service>
5858

59+
<service id="session.abstract_handler" class="Symfony\Component\HttpFoundation\Session\Storage\Handler\AbstractSessionHandler">
60+
<factory class="Symfony\Component\HttpFoundation\Session\Storage\Handler\SessionHandlerFactory" method="createHandler" />
61+
<argument />
62+
</service>
63+
5964
<service id="session_listener" class="Symfony\Component\HttpKernel\EventListener\SessionListener">
6065
<tag name="kernel.event_subscriber" />
6166
<argument type="service_locator">

src/Symfony/Bundle/FrameworkBundle/composer.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@
2222
"symfony/config": "^4.3.4|^5.0",
2323
"symfony/dependency-injection": "^4.4|^5.0",
2424
"symfony/error-renderer": "^4.4|^5.0",
25-
"symfony/http-foundation": "^4.3|^5.0",
25+
"symfony/http-foundation": "^4.4|^5.0",
2626
"symfony/http-kernel": "^4.4",
2727
"symfony/polyfill-mbstring": "~1.0",
2828
"symfony/filesystem": "^3.4|^4.0|^5.0",

src/Symfony/Component/HttpFoundation/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ CHANGELOG
1010
* `PdoSessionHandler` now precalculates the expiry timestamp in the lifetime column,
1111
make sure to run `CREATE INDEX EXPIRY ON sessions (sess_lifetime)` to update your database
1212
to speed up garbage collection of expired sessions.
13+
* added `SessionHandlerFactory` to create session handlers with a DSN
1314

1415
4.3.0
1516
-----
Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Component\HttpFoundation\Session\Storage\Handler;
13+
14+
use Doctrine\DBAL\DriverManager;
15+
use Symfony\Component\Cache\Adapter\AbstractAdapter;
16+
use Symfony\Component\Cache\Traits\RedisClusterProxy;
17+
use Symfony\Component\Cache\Traits\RedisProxy;
18+
19+
/**
20+
* This abstract session handler provides a generic implementation
21+
* of the PHP 7.0 SessionUpdateTimestampHandlerInterface,
22+
* enabling strict and lazy session handling.
23+
*
24+
* @author Nicolas Grekas <p@tchwork.com>
25+
*/
26+
class SessionHandlerFactory
27+
{
28+
/**
29+
* @param \Redis|\RedisArray|\RedisCluster|\Predis\ClientInterface|\Memcached|\PDO|string $connection Connection or DSN
30+
*/
31+
public static function createHandler($connection): AbstractSessionHandler
32+
{
33+
if (!\is_string($connection) && !\is_object($connection)) {
34+
throw new \TypeError(sprintf('Argument 1 passed to %s() must be a string or a connection object, %s given.', __METHOD__, \gettype($connection)));
35+
}
36+
37+
switch (true) {
38+
case $connection instanceof \Redis:
39+
case $connection instanceof \RedisArray:
40+
case $connection instanceof \RedisCluster:
41+
case $connection instanceof \Predis\ClientInterface:
42+
case $connection instanceof RedisProxy:
43+
case $connection instanceof RedisClusterProxy:
44+
return new RedisSessionHandler($connection);
45+
46+
case $connection instanceof \Memcached:
47+
return new MemcachedSessionHandler($connection);
48+
49+
case $connection instanceof \PDO:
50+
return new PdoSessionHandler($connection);
51+
52+
case !\is_string($connection):
53+
throw new \InvalidArgumentException(sprintf('Unsupported Connection: %s.', \get_class($connection)));
54+
55+
case 0 === strpos($connection, 'file://'):
56+
return new StrictSessionHandler(new NativeFileSessionHandler(substr($connection, 7)));
57+
58+
case 0 === strpos($connection, 'redis://') && class_exists(AbstractAdapter::class):
59+
case 0 === strpos($connection, 'rediss://') && class_exists(AbstractAdapter::class):
60+
return new RedisSessionHandler(AbstractAdapter::createConnection($connection, ['lazy' => true]));
61+
62+
case 0 === strpos($connection, 'memcached://') && class_exists(AbstractAdapter::class):
63+
return new MemcachedSessionHandler(AbstractAdapter::createConnection($connection, ['lazy' => true]));
64+
65+
case 0 === strpos($connection, 'pdo_oci://'):
66+
if (!class_exists(DriverManager::class)) {
67+
throw new \LogicException(sprintf('Failed to parse the DSN "%s". Try running "composer require doctrine/dbal".', $connection));
68+
}
69+
$connection = DriverManager::getConnection(['url' => $connection])->getWrappedConnection();
70+
// no break;
71+
72+
case 0 === strpos($connection, 'mssql://'):
73+
case 0 === strpos($connection, 'mysql://'):
74+
case 0 === strpos($connection, 'mysql2://'):
75+
case 0 === strpos($connection, 'pgsql://'):
76+
case 0 === strpos($connection, 'postgres://'):
77+
case 0 === strpos($connection, 'postgresql://'):
78+
case 0 === strpos($connection, 'sqlsrv://'):
79+
case 0 === strpos($connection, 'sqlite://'):
80+
case 0 === strpos($connection, 'sqlite3://'):
81+
return new PdoSessionHandler($connection);
82+
}
83+
84+
throw new \InvalidArgumentException(sprintf('Unsupported Connection: %s.', $connection));
85+
}
86+
}

src/Symfony/Component/Lock/Store/PdoStore.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -232,7 +232,7 @@ private function getConnection()
232232
if (null === $this->conn) {
233233
if (strpos($this->dsn, '://')) {
234234
if (!class_exists(DriverManager::class)) {
235-
throw new InvalidArgumentException(sprintf('Failed to parse the DSN "%s". Try running "composer require doctrine/dbal".', $this->dsn));
235+
throw new \LogicException(sprintf('Failed to parse the DSN "%s". Try running "composer require doctrine/dbal".', $this->dsn));
236236
}
237237
$this->conn = DriverManager::getConnection(['url' => $this->dsn]);
238238
} else {

src/Symfony/Component/Lock/Store/StoreFactory.php

Lines changed: 41 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111

1212
namespace Symfony\Component\Lock\Store;
1313

14+
use Doctrine\DBAL\Connection;
1415
use Symfony\Component\Cache\Adapter\AbstractAdapter;
1516
use Symfony\Component\Cache\Traits\RedisClusterProxy;
1617
use Symfony\Component\Cache\Traits\RedisProxy;
@@ -25,59 +26,72 @@
2526
class StoreFactory
2627
{
2728
/**
28-
* @param \Redis|\RedisArray|\RedisCluster|\Predis\ClientInterface|\Memcached|\Zookeeper|string $connection Connection or DSN or Store short name
29+
* @param \Redis|\RedisArray|\RedisCluster|\Predis\ClientInterface|\Memcached|\PDO|Connection|\Zookeeper|string $connection Connection or DSN or Store short name
2930
*
3031
* @return PersistingStoreInterface
3132
*/
3233
public static function createStore($connection)
3334
{
34-
if (
35-
$connection instanceof \Redis ||
36-
$connection instanceof \RedisArray ||
37-
$connection instanceof \RedisCluster ||
38-
$connection instanceof \Predis\ClientInterface ||
39-
$connection instanceof RedisProxy ||
40-
$connection instanceof RedisClusterProxy
41-
) {
42-
return new RedisStore($connection);
43-
}
44-
if ($connection instanceof \Memcached) {
45-
return new MemcachedStore($connection);
46-
}
47-
if ($connection instanceof \Zookeeper) {
48-
return new ZookeeperStore($connection);
49-
}
50-
if (!\is_string($connection)) {
51-
throw new InvalidArgumentException(sprintf('Unsupported Connection: %s.', \get_class($connection)));
35+
if (!\is_string($connection) && !\is_object($connection)) {
36+
throw new \TypeError(sprintf('Argument 1 passed to %s() must be a string or a connection object, %s given.', __METHOD__, \gettype($connection)));
5237
}
5338

5439
switch (true) {
40+
case $connection instanceof \Redis:
41+
case $connection instanceof \RedisArray:
42+
case $connection instanceof \RedisCluster:
43+
case $connection instanceof \Predis\ClientInterface:
44+
case $connection instanceof RedisProxy:
45+
case $connection instanceof RedisClusterProxy:
46+
return new RedisStore($connection);
47+
48+
case $connection instanceof \Memcached:
49+
return new MemcachedStore($connection);
50+
51+
case $connection instanceof \PDO:
52+
case $connection instanceof Connection:
53+
return new PdoStore($connection);
54+
55+
case $connection instanceof \Zookeeper:
56+
return new ZookeeperStore($connection);
57+
58+
case !\is_string($connection):
59+
throw new InvalidArgumentException(sprintf('Unsupported Connection: %s.', \get_class($connection)));
60+
5561
case 'flock' === $connection:
5662
return new FlockStore();
63+
5764
case 0 === strpos($connection, 'flock://'):
5865
return new FlockStore(substr($connection, 8));
66+
5967
case 'semaphore' === $connection:
6068
return new SemaphoreStore();
69+
6170
case 0 === strpos($connection, 'redis://') && class_exists(AbstractAdapter::class):
6271
case 0 === strpos($connection, 'rediss://') && class_exists(AbstractAdapter::class):
6372
return new RedisStore(AbstractAdapter::createConnection($connection, ['lazy' => true]));
73+
6474
case 0 === strpos($connection, 'memcached://') && class_exists(AbstractAdapter::class):
6575
return new MemcachedStore(AbstractAdapter::createConnection($connection, ['lazy' => true]));
66-
case 0 === strpos($connection, 'sqlite:'):
76+
77+
case 0 === strpos($connection, 'mssql://'):
6778
case 0 === strpos($connection, 'mysql:'):
68-
case 0 === strpos($connection, 'pgsql:'):
69-
case 0 === strpos($connection, 'oci:'):
70-
case 0 === strpos($connection, 'sqlsrv:'):
71-
case 0 === strpos($connection, 'sqlite3://'):
7279
case 0 === strpos($connection, 'mysql2://'):
80+
case 0 === strpos($connection, 'oci:'):
81+
case 0 === strpos($connection, 'oci8://'):
82+
case 0 === strpos($connection, 'pdo_oci://'):
83+
case 0 === strpos($connection, 'pgsql:'):
7384
case 0 === strpos($connection, 'postgres://'):
7485
case 0 === strpos($connection, 'postgresql://'):
75-
case 0 === strpos($connection, 'mssql://'):
86+
case 0 === strpos($connection, 'sqlsrv:'):
87+
case 0 === strpos($connection, 'sqlite:'):
88+
case 0 === strpos($connection, 'sqlite3://'):
7689
return new PdoStore($connection);
90+
7791
case 0 === strpos($connection, 'zookeeper://'):
7892
return new ZookeeperStore(ZookeeperStore::createConnection($connection));
79-
default:
80-
throw new InvalidArgumentException(sprintf('Unsupported Connection: %s.', $connection));
8193
}
94+
95+
throw new InvalidArgumentException(sprintf('Unsupported Connection: %s.', $connection));
8296
}
8397
}

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy