Drupal 8 覆盖会话管理
Drupal 8 overriding session management
我想覆盖 drupals 核心会话管理以支持我自己的,而不是将会话保存到 Redis 而不是数据库。
谷歌搜索后,除此之外没什么可做的:
https://www.drupal.org/project/session_proxy
唯一的问题是它与 Drupal 8 不兼容,我只想保存到 Redis,不需要任何其他处理程序。
我在 Symfony 中创建了一个会话处理程序服务,但在 Drupal 8 中似乎更加棘手。
关于我应该如何进行的任何建议?
谢谢
Redis 模块有一个 alpha 版本。如果它当前的限制不是 show-stoppers,那么您可以使用它并按照文档进行配置。
https://www.drupal.org/project/redis
有关配置设置的完整详细信息,请参阅文档,但作为初学者,在安装模块后,您可以将类似的内容添加到 settings.php
$settings['cache']['default'] = 'cache.backend.redis';
$settings['redis.connection']['host'] = '<<redis_host>>';
$settings['redis.connection']['port'] = '<<redis_port>>';
其中'redis_host'和'redis_port'根据你的Redis实例设置。
我认为在不依赖第 3 方模块或任何其他插件的情况下解决此问题的最简单方法是覆盖 Drupals 核心 SessionHandler class。
首先,在我的模块中,我创建了一个 ServiceProvider class,它指示容器用我自己的定义重新定义核心 SessionHandler class。我不需要数据库连接服务,所以我确保只有请求堆栈被传递给构造函数。
<?php
namespace Drupal\my_module;
use Drupal\Core\DependencyInjection\ContainerBuilder;
use Drupal\Core\DependencyInjection\ServiceProviderBase;
use Symfony\Component\DependencyInjection\Reference;
class OoAuthServiceProvider extends ServiceProviderBase
{
/**
* {@inheritdoc}
*/
public function alter(ContainerBuilder $container)
{
$container->getDefinition('session_handler.storage')
->setClass('Drupal\my_module\SessionHandler')
->setArguments([
new Reference('request_stack')
]);
}
}
然后我开始创建自己的 Redis SessionHandler:
<?php
namespace Drupal\my_module;
use Drupal\Component\Utility\Crypt;
use Drupal\Core\DependencyInjection\DependencySerializationTrait;
use Drupal\Core\Utility\Error;
use Symfony\Component\HttpFoundation\RequestStack;
use Symfony\Component\HttpFoundation\Session\Storage\Proxy\AbstractProxy;
/**
* Default session handler.
*/
class SessionHandler extends AbstractProxy implements \SessionHandlerInterface {
use DependencySerializationTrait;
/**
* The request stack.
*
* @var RequestStack
*/
protected $requestStack;
/**
* @var \Redis
*/
protected $redis;
/**
* SessionHandler constructor.
*
* @param RequestStack $requestStack
*/
public function __construct(RequestStack $requestStack)
{
$this->requestStack = $requestStack;
// TODO: Store redis connection details in config.
$this->redis = (new PhpRedis())->getClient('redis-host', 6379);
}
/**
* {@inheritdoc}
*/
public function open($savePath, $name)
{
return true;
}
/**
* {@inheritdoc}
*/
public function read($sid)
{
$data = '';
if (!empty($sid)) {
$query = $this->redis->get(Crypt::hashBase64($sid));
$data = unserialize($query);
}
return (string) $data['session'];
}
/**
* {@inheritdoc}
*/
public function write($sid, $value)
{
// The exception handler is not active at this point, so we need to do it
// manually.
var_dump(['Value', $value]);
try {
$request = $this->requestStack->getCurrentRequest();
$fields = [
'uid' => $request->getSession()->get('uid', 0),
'hostname' => $request->getClientIP(),
'session' => $value,
'timestamp' => REQUEST_TIME,
];
$this->redis->set(
Crypt::hashBase64($sid),
serialize($fields),
(int) ini_get("session.gc_maxlifetime")
);
return true;
}
catch (\Exception $exception) {
require_once DRUPAL_ROOT . '/core/includes/errors.inc';
// If we are displaying errors, then do so with no possibility of a
// further uncaught exception being thrown.
if (error_displayable()) {
print '<h1>Uncaught exception thrown in session handler.</h1>';
print '<p>' . Error::renderExceptionSafe($exception) . '</p><hr />';
}
return true;
}
}
/**
* {@inheritdoc}
*/
public function close()
{
return true;
}
/**
* {@inheritdoc}
*/
public function destroy($sid)
{
// Delete session data.
$this->redis->delete(Crypt::hashBase64($sid));
return true;
}
/**
* {@inheritdoc}
*/
public function gc($lifetime)
{
// Redundant method when using Redis. You no longer have to check the session
// timestamp as the session.gc_maxlifetime is set as TTL on write.
return true;
}
}
在我自己的 SessionHandler 实现中使用的 PhpRedis 只是一个用于处理连接到 Redis 的小实用程序class。
<?php
namespace Drupal\my_module;
/**
* Class PhpRedis
* @package Drupal\oo_auth
*/
class PhpRedis implements ClientInterface
{
/**
* {@inheritdoc}
*/
public function getClient($host = null, $port = null, $base = null, $password = null)
{
$client = new \Redis();
$client->connect($host, $port);
if (isset($password)) {
$client->auth($password);
}
if (isset($base)) {
$client->select($base);
}
// Do not allow PhpRedis serialize itself data, we are going to do it
// oneself. This will ensure less memory footprint on Redis size when
// we will attempt to store small values.
$client->setOption(\Redis::OPT_SERIALIZER, \Redis::SERIALIZER_NONE);
return $client;
}
/**
* {@inheritdoc}
*/
public function getName() {
return 'PhpRedis';
}
}
<?php
namespace Drupal\my_module;
/**
* Interface ClientInterface
* @package Drupal\oo_auth
*/
interface ClientInterface
{
/**
* Get the connected client instance.
*
* @param null $host
* @param null $port
* @param null $base
*
* @return mixed
*/
public function getClient($host = NULL, $port = NULL, $base = NULL);
/**
* Get underlying library name used.
*
* This can be useful for contribution code that may work with only some of
* the provided clients.
*
* @return string
*/
public function getName();
}
没有(我能找到的)建议文档向您举例说明如何使用 Redis(这实际上适用于任何数据存储)作为 Drupal 安装的会话存储。有关于如何启动它的帖子和 运行 其他第 3 方模块很好,但我不想要额外的绒毛。
我想覆盖 drupals 核心会话管理以支持我自己的,而不是将会话保存到 Redis 而不是数据库。
谷歌搜索后,除此之外没什么可做的: https://www.drupal.org/project/session_proxy
唯一的问题是它与 Drupal 8 不兼容,我只想保存到 Redis,不需要任何其他处理程序。
我在 Symfony 中创建了一个会话处理程序服务,但在 Drupal 8 中似乎更加棘手。
关于我应该如何进行的任何建议?
谢谢
Redis 模块有一个 alpha 版本。如果它当前的限制不是 show-stoppers,那么您可以使用它并按照文档进行配置。 https://www.drupal.org/project/redis
有关配置设置的完整详细信息,请参阅文档,但作为初学者,在安装模块后,您可以将类似的内容添加到 settings.php
$settings['cache']['default'] = 'cache.backend.redis';
$settings['redis.connection']['host'] = '<<redis_host>>';
$settings['redis.connection']['port'] = '<<redis_port>>';
其中'redis_host'和'redis_port'根据你的Redis实例设置。
我认为在不依赖第 3 方模块或任何其他插件的情况下解决此问题的最简单方法是覆盖 Drupals 核心 SessionHandler class。
首先,在我的模块中,我创建了一个 ServiceProvider class,它指示容器用我自己的定义重新定义核心 SessionHandler class。我不需要数据库连接服务,所以我确保只有请求堆栈被传递给构造函数。
<?php
namespace Drupal\my_module;
use Drupal\Core\DependencyInjection\ContainerBuilder;
use Drupal\Core\DependencyInjection\ServiceProviderBase;
use Symfony\Component\DependencyInjection\Reference;
class OoAuthServiceProvider extends ServiceProviderBase
{
/**
* {@inheritdoc}
*/
public function alter(ContainerBuilder $container)
{
$container->getDefinition('session_handler.storage')
->setClass('Drupal\my_module\SessionHandler')
->setArguments([
new Reference('request_stack')
]);
}
}
然后我开始创建自己的 Redis SessionHandler:
<?php
namespace Drupal\my_module;
use Drupal\Component\Utility\Crypt;
use Drupal\Core\DependencyInjection\DependencySerializationTrait;
use Drupal\Core\Utility\Error;
use Symfony\Component\HttpFoundation\RequestStack;
use Symfony\Component\HttpFoundation\Session\Storage\Proxy\AbstractProxy;
/**
* Default session handler.
*/
class SessionHandler extends AbstractProxy implements \SessionHandlerInterface {
use DependencySerializationTrait;
/**
* The request stack.
*
* @var RequestStack
*/
protected $requestStack;
/**
* @var \Redis
*/
protected $redis;
/**
* SessionHandler constructor.
*
* @param RequestStack $requestStack
*/
public function __construct(RequestStack $requestStack)
{
$this->requestStack = $requestStack;
// TODO: Store redis connection details in config.
$this->redis = (new PhpRedis())->getClient('redis-host', 6379);
}
/**
* {@inheritdoc}
*/
public function open($savePath, $name)
{
return true;
}
/**
* {@inheritdoc}
*/
public function read($sid)
{
$data = '';
if (!empty($sid)) {
$query = $this->redis->get(Crypt::hashBase64($sid));
$data = unserialize($query);
}
return (string) $data['session'];
}
/**
* {@inheritdoc}
*/
public function write($sid, $value)
{
// The exception handler is not active at this point, so we need to do it
// manually.
var_dump(['Value', $value]);
try {
$request = $this->requestStack->getCurrentRequest();
$fields = [
'uid' => $request->getSession()->get('uid', 0),
'hostname' => $request->getClientIP(),
'session' => $value,
'timestamp' => REQUEST_TIME,
];
$this->redis->set(
Crypt::hashBase64($sid),
serialize($fields),
(int) ini_get("session.gc_maxlifetime")
);
return true;
}
catch (\Exception $exception) {
require_once DRUPAL_ROOT . '/core/includes/errors.inc';
// If we are displaying errors, then do so with no possibility of a
// further uncaught exception being thrown.
if (error_displayable()) {
print '<h1>Uncaught exception thrown in session handler.</h1>';
print '<p>' . Error::renderExceptionSafe($exception) . '</p><hr />';
}
return true;
}
}
/**
* {@inheritdoc}
*/
public function close()
{
return true;
}
/**
* {@inheritdoc}
*/
public function destroy($sid)
{
// Delete session data.
$this->redis->delete(Crypt::hashBase64($sid));
return true;
}
/**
* {@inheritdoc}
*/
public function gc($lifetime)
{
// Redundant method when using Redis. You no longer have to check the session
// timestamp as the session.gc_maxlifetime is set as TTL on write.
return true;
}
}
在我自己的 SessionHandler 实现中使用的 PhpRedis 只是一个用于处理连接到 Redis 的小实用程序class。
<?php
namespace Drupal\my_module;
/**
* Class PhpRedis
* @package Drupal\oo_auth
*/
class PhpRedis implements ClientInterface
{
/**
* {@inheritdoc}
*/
public function getClient($host = null, $port = null, $base = null, $password = null)
{
$client = new \Redis();
$client->connect($host, $port);
if (isset($password)) {
$client->auth($password);
}
if (isset($base)) {
$client->select($base);
}
// Do not allow PhpRedis serialize itself data, we are going to do it
// oneself. This will ensure less memory footprint on Redis size when
// we will attempt to store small values.
$client->setOption(\Redis::OPT_SERIALIZER, \Redis::SERIALIZER_NONE);
return $client;
}
/**
* {@inheritdoc}
*/
public function getName() {
return 'PhpRedis';
}
}
<?php
namespace Drupal\my_module;
/**
* Interface ClientInterface
* @package Drupal\oo_auth
*/
interface ClientInterface
{
/**
* Get the connected client instance.
*
* @param null $host
* @param null $port
* @param null $base
*
* @return mixed
*/
public function getClient($host = NULL, $port = NULL, $base = NULL);
/**
* Get underlying library name used.
*
* This can be useful for contribution code that may work with only some of
* the provided clients.
*
* @return string
*/
public function getName();
}
没有(我能找到的)建议文档向您举例说明如何使用 Redis(这实际上适用于任何数据存储)作为 Drupal 安装的会话存储。有关于如何启动它的帖子和 运行 其他第 3 方模块很好,但我不想要额外的绒毛。