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 方模块很好,但我不想要额外的绒毛。