Drupal 8覆盖会话管理

时间:2017-01-05 13:38:12

标签: php session drupal drupal-8

我想覆盖drupals核心会话管理而不是我自己,而是将会话保存到Redis而不是数据库。

谷歌搜索后除了这个以外没有太多事情要做: https://www.drupal.org/project/session_proxy

唯一的问题是与Drupal 8不兼容,我只想保存到Redis我不需要任何其他处理程序。

在Symfony中,我创建了一个会话处理程序服务,但在Drupal 8中似乎更加棘手。

关于我应该如何进行的任何建议?

由于

2 个答案:

答案 0 :(得分:3)

以下是我认为在不依赖第三方模块或任何其他插件的情况下解决此问题的最简单方法是覆盖Drupals核心SessionHandler类。

首先在我的模块中,我创建了一个ServiceProvider类,它指示容器用我自己的方法重新定义核心SessionHandler类定义。我不需要数据库连接服务,因此我确保只将请求堆栈传递给构造函数。

<?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的连接。

<?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安装的会话存储的示例。有关于如何使用其他第三方模块启动和运行的帖子很好,但我不想要额外的毛病。

答案 1 :(得分:2)

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实例设置'redis_host'和'redis_port'。