Skip to content

Commit f676184

Browse files
authored
feat: customizable session driver (#3610)
1 parent 84c3116 commit f676184

7 files changed

Lines changed: 340 additions & 9 deletions

File tree

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
<?php
2+
3+
/*
4+
* This file is part of Flarum.
5+
*
6+
* For detailed copyright and license information, please view the
7+
* LICENSE file that was distributed with this source code.
8+
*/
9+
10+
namespace Flarum\Extend;
11+
12+
use Flarum\Extension\Extension;
13+
use Illuminate\Contracts\Container\Container;
14+
15+
class Session implements ExtenderInterface
16+
{
17+
private $drivers = [];
18+
19+
/**
20+
* Register a new session driver.
21+
*
22+
* A driver can currently be selected by setting `session.driver` in `config.php`.
23+
*
24+
* @param string $name: The name of the driver.
25+
* @param string $driverClass: The ::class attribute of the driver.
26+
* Driver must implement `\Flarum\User\SessionDriverInterface`.
27+
* @return self
28+
*/
29+
public function driver(string $name, string $driverClass): self
30+
{
31+
$this->drivers[$name] = $driverClass;
32+
33+
return $this;
34+
}
35+
36+
public function extend(Container $container, Extension $extension = null)
37+
{
38+
$container->extend('flarum.session.drivers', function ($drivers) {
39+
return array_merge($drivers, $this->drivers);
40+
});
41+
}
42+
}

framework/core/src/Foundation/Console/InfoCommand.php

Lines changed: 69 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,14 @@
1414
use Flarum\Foundation\Application;
1515
use Flarum\Foundation\Config;
1616
use Flarum\Settings\SettingsRepositoryInterface;
17+
use Flarum\User\SessionManager;
1718
use Illuminate\Contracts\Queue\Queue;
1819
use Illuminate\Database\ConnectionInterface;
20+
use Illuminate\Support\Arr;
1921
use Illuminate\Support\Str;
22+
use InvalidArgumentException;
2023
use PDO;
24+
use SessionHandlerInterface;
2125
use Symfony\Component\Console\Helper\Table;
2226
use Symfony\Component\Console\Helper\TableStyle;
2327

@@ -48,18 +52,32 @@ class InfoCommand extends AbstractCommand
4852
*/
4953
private $queue;
5054

55+
/**
56+
* @var SessionManager
57+
*/
58+
private $session;
59+
60+
/**
61+
* @var SessionHandlerInterface
62+
*/
63+
private $sessionHandler;
64+
5165
public function __construct(
5266
ExtensionManager $extensions,
5367
Config $config,
5468
SettingsRepositoryInterface $settings,
5569
ConnectionInterface $db,
56-
Queue $queue
70+
Queue $queue,
71+
SessionManager $session,
72+
SessionHandlerInterface $sessionHandler
5773
) {
5874
$this->extensions = $extensions;
5975
$this->config = $config;
6076
$this->settings = $settings;
6177
$this->db = $db;
6278
$this->queue = $queue;
79+
$this->session = $session;
80+
$this->sessionHandler = $sessionHandler;
6381

6482
parent::__construct();
6583
}
@@ -93,6 +111,7 @@ protected function fire()
93111
$this->output->writeln('<info>Base URL:</info> '.$this->config->url());
94112
$this->output->writeln('<info>Installation path:</info> '.getcwd());
95113
$this->output->writeln('<info>Queue driver:</info> '.$this->identifyQueueDriver());
114+
$this->output->writeln('<info>Session driver:</info> '.$this->identifySessionDriver());
96115
$this->output->writeln('<info>Mail driver:</info> '.$this->settings->get('mail_driver', 'unknown'));
97116
$this->output->writeln('<info>Debug mode:</info> '.($this->config->inDebugMode() ? '<error>ON</error>' : 'off'));
98117

@@ -169,4 +188,53 @@ private function identifyDatabaseVersion(): string
169188
{
170189
return $this->db->getPdo()->getAttribute(PDO::ATTR_SERVER_VERSION);
171190
}
191+
192+
/**
193+
* Reports on the session driver in use based on three scenarios:
194+
* 1. If the configured session driver is valid and in use, it will be returned.
195+
* 2. If the configured session driver is invalid, fallback to the default one and mention it.
196+
* 3. If the actual used driver (i.e `session.handler`) is different from the current one (configured or default), mention it.
197+
*/
198+
private function identifySessionDriver(): string
199+
{
200+
/*
201+
* Get the configured driver and fallback to the default one.
202+
*/
203+
$defaultDriver = $this->session->getDefaultDriver();
204+
$configuredDriver = Arr::get($this->config, 'session.driver', $defaultDriver);
205+
$driver = $configuredDriver;
206+
207+
try {
208+
// Try to get the configured driver instance.
209+
// Driver instances are created on demand.
210+
$this->session->driver($configuredDriver);
211+
} catch (InvalidArgumentException $e) {
212+
// An exception is thrown if the configured driver is not a valid driver.
213+
// So we fallback to the default driver.
214+
$driver = $defaultDriver;
215+
}
216+
217+
/*
218+
* Get actual driver name from its class name.
219+
* And compare that to the current configured driver.
220+
*/
221+
// Get class name
222+
$handlerName = get_class($this->sessionHandler);
223+
// Drop the namespace
224+
$handlerName = Str::afterLast($handlerName, '\\');
225+
// Lowercase the class name
226+
$handlerName = strtolower($handlerName);
227+
// Drop everything like sessionhandler FileSessionHandler, DatabaseSessionHandler ..etc
228+
$handlerName = str_replace('sessionhandler', '', $handlerName);
229+
230+
if ($driver !== $handlerName) {
231+
return "$handlerName <comment>(Code override. Configured to <options=bold,underscore>$configuredDriver</>)</comment>";
232+
}
233+
234+
if ($driver !== $configuredDriver) {
235+
return "$driver <comment>(Fallback default driver. Configured to invalid driver <options=bold,underscore>$configuredDriver</>)</comment>";
236+
}
237+
238+
return $driver;
239+
}
172240
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
<?php
2+
3+
/*
4+
* This file is part of Flarum.
5+
*
6+
* For detailed copyright and license information, please view the
7+
* LICENSE file that was distributed with this source code.
8+
*/
9+
10+
namespace Flarum\User;
11+
12+
use Flarum\Foundation\Config;
13+
use Flarum\Settings\SettingsRepositoryInterface;
14+
use SessionHandlerInterface;
15+
16+
interface SessionDriverInterface
17+
{
18+
/**
19+
* Build a session handler to handle sessions.
20+
* Settings and configuration can either be pulled from the Flarum settings repository
21+
* or the config.php file.
22+
*
23+
* @param SettingsRepositoryInterface $settings: An instance of the Flarum settings repository.
24+
* @param Config $config: An instance of the wrapper class around `config.php`.
25+
*/
26+
public function build(SettingsRepositoryInterface $settings, Config $config): SessionHandlerInterface;
27+
}
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
<?php
2+
3+
/*
4+
* This file is part of Flarum.
5+
*
6+
* For detailed copyright and license information, please view the
7+
* LICENSE file that was distributed with this source code.
8+
*/
9+
10+
namespace Flarum\User;
11+
12+
use Flarum\Foundation\Config;
13+
use Illuminate\Session\SessionManager as IlluminateSessionManager;
14+
use Illuminate\Support\Arr;
15+
use InvalidArgumentException;
16+
use Psr\Log\LoggerInterface;
17+
use SessionHandlerInterface;
18+
19+
class SessionManager extends IlluminateSessionManager
20+
{
21+
/**
22+
* Returns the configured session handler.
23+
* Picks up the driver from `config.php` using the `session.driver` item.
24+
* Falls back to the default driver if the configured one is not available,
25+
* and logs a critical error in that case.
26+
*/
27+
public function handler(): SessionHandlerInterface
28+
{
29+
$config = $this->container->make(Config::class);
30+
$driverName = Arr::get($config, 'session.driver');
31+
32+
try {
33+
$driverInstance = parent::driver($driverName);
34+
} catch (InvalidArgumentException $e) {
35+
$defaultDriverName = $this->getDefaultDriver();
36+
$driverInstance = parent::driver($defaultDriverName);
37+
38+
// But we will log a critical error to the webmaster.
39+
$this->container->make(LoggerInterface::class)->critical(
40+
"The configured session driver [$driverName] is not available. Falling back to default [$defaultDriverName]. Please check your configuration."
41+
);
42+
}
43+
44+
return $driverInstance->getHandler();
45+
}
46+
}

framework/core/src/User/SessionServiceProvider.php

Lines changed: 39 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,9 @@
1010
namespace Flarum\User;
1111

1212
use Flarum\Foundation\AbstractServiceProvider;
13-
use Illuminate\Session\FileSessionHandler;
13+
use Flarum\Foundation\Config;
14+
use Flarum\Settings\SettingsRepositoryInterface;
15+
use Illuminate\Contracts\Container\Container;
1416
use SessionHandlerInterface;
1517

1618
class SessionServiceProvider extends AbstractServiceProvider
@@ -20,12 +22,42 @@ class SessionServiceProvider extends AbstractServiceProvider
2022
*/
2123
public function register()
2224
{
23-
$this->container->singleton('session.handler', function ($container) {
24-
return new FileSessionHandler(
25-
$container['files'],
26-
$container['config']['session.files'],
27-
$container['config']['session.lifetime']
28-
);
25+
$this->container->singleton('flarum.session.drivers', function () {
26+
return [];
27+
});
28+
29+
$this->container->singleton('session', function (Container $container) {
30+
$manager = new SessionManager($container);
31+
$drivers = $container->make('flarum.session.drivers');
32+
$settings = $container->make(SettingsRepositoryInterface::class);
33+
$config = $container->make(Config::class);
34+
35+
/**
36+
* Default to the file driver already defined by Laravel.
37+
*
38+
* @see \Illuminate\Session\SessionManager::createFileDriver()
39+
*/
40+
$manager->setDefaultDriver('file');
41+
42+
foreach ($drivers as $driver => $className) {
43+
/** @var SessionDriverInterface $driverInstance */
44+
$driverInstance = $container->make($className);
45+
46+
$manager->extend($driver, function () use ($settings, $config, $driverInstance) {
47+
return $driverInstance->build($settings, $config);
48+
});
49+
}
50+
51+
return $manager;
52+
});
53+
54+
$this->container->alias('session', SessionManager::class);
55+
56+
$this->container->singleton('session.handler', function (Container $container): SessionHandlerInterface {
57+
/** @var SessionManager $manager */
58+
$manager = $container->make('session');
59+
60+
return $manager->handler();
2961
});
3062

3163
$this->container->alias('session.handler', SessionHandlerInterface::class);

0 commit comments

Comments
 (0)