【问题标题】:Drupal 8 overriding session managementDrupal 8 覆盖会话管理
【发布时间】:2017-01-05 13:38:12
【问题描述】:

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

在谷歌搜索之后,除了以下内容没有什么可做的: https://www.drupal.org/project/session_proxy

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

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

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

谢谢

【问题讨论】:

    标签: php session drupal drupal-8


    【解决方案1】:

    我认为在不依赖 3rd 方模块或任何其他插件的情况下解决此问题的最简单方法是覆盖 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 安装的会话存储的示例。有关于如何启动它并与其他 3rd 方模块一起运行的帖子,这很好,但我不想要额外的绒毛。

    【讨论】:

    • 嘿 Kal,你的模块有一个 zip 吗?我想看看能否在我的 Drupal 项目中实现它。
    • 嘿安德烈,我不遗憾,因为我以前工作的公司就是这样。但是,如果您需要帮助,请随时给我发电子邮件,我会尽力提供帮助:justlikephp@gmail.com :)
    【解决方案2】:

    Redis 模块有一个 alpha 版本。如果它的当前限制不是显示停止器,那么您可以使用它并按照文档进行配置。 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 实例设置的。

    【讨论】:

    • 我知道它说“缓存”,但它也会在 redis 中粘贴会话吗?
    • 我在使用这个 Redis 模块时遇到了同样的问题。我设法使用 Redis 作为缓存来获取它,并且效果很好。但是,尝试在 Redis 中获取会话仍然没有运气
    • D8 Redis 模块的当前版本不支持会话,但有一个子模块可以支持。看到这个线程drupal.org/project/redis/issues/2876099
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2015-08-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-03-12
    • 2020-01-21
    • 1970-01-01
    相关资源
    最近更新 更多