【问题标题】:Symfony 2 and custom session variables from legacy applicationSymfony 2 和来自遗留应用程序的自定义会话变量
【发布时间】:2016-06-06 17:05:16
【问题描述】:

我正在设置从旧代码库迁移到 Symfony 代码库的能力,并且我正在尝试在两个应用程序之间共享旧会话变量。

我目前可以在app_dev.php 中使用var_dump($_SESSION),而user_id 密钥来自旧版应用程序。但是,当我在控制器中运行以下命令时,我没有得到任何会话变量:

var_dump($request->getSession()->all());

所以澄清一下,我目前可以访问$_SESSION['user_id'],所以会话在应用程序之间成功共享,但 Symfony 会话对象及其参数包不包含旧密钥。

我的目标:

  • user_id 密钥在$request->getSession() 调用中可用
  • 此处提供所有其他密钥

我的问题:

我已经尝试过PHP Session bridge,但我认为这不应该达到我想要的效果。添加 bridge 参数不仅在我的 Symfony 应用程序中没有任何作用,而且从我的read 来看,这意味着以某种方式在其他遗留应用程序中使用,而不是在 Symfony 中。

Symfony 会话在特殊的 'Bags' 中存储属性等数据,这些数据使用 $_SESSION 超全局中的键。这意味着 Symfony 会话不能访问 $_SESSION 中可能由遗留应用程序设置的任意键,尽管所有 $_SESSION 内容将在保存会话时保存。 [来自Integrating with legacy sessions]

我还查看了TheodoEvolutionSessionBundle,但这是旧的,没有得到积极的支持或工作,并且不适用于 Symfony 3。

我注意到 Symfony 将其会话数据存储在 $_SESSION['_sf2_attributes'] 下,所以我最初的想法是在 app_dev.php 中执行此操作:

$_SESSION['_sf2_attributes']['user_id'] = $_SESSION['user_id'];

显然是错误的做法,因为我也必须在那里打电话给session_start()

我如何将旧的 $_SESSION 数据迁移到 Symfony 的 Session 对象中? Symfony 是否有内置的东西可以帮助解决这个问题或可用的捆绑包?如果没有,在哪里我可以将我的自定义 $_SESSION['_sf2_attributes'] 'hack' 放在正确的位置来处理所有这些密钥的迁移?

【问题讨论】:

标签: php symfony session


【解决方案1】:

Symfony 在会话中引入了会话包的概念,因此所有内容都是命名空间的。没有用于访问非命名空间会话属性的内置解决方案。

解决方案是使用标量包,就像TheodoEvolutionSessionBundle 一样。我不会直接使用它,而是实现一些对你的项目有用的自定义(它们只提供 symfony1 和 codeigniter 的集成)。以他们的想法为基础,但根据您的需要进行调整。

或者,您可以实现一个kernel.request 监听器,它将旧会话属性重写为 Symfony 的:

if (isset($_SESSION['user_id'])) {
    $event->getRequest()->getSession()->set('user_id', $_SESSION['user_id']);
}

吉姆的编辑

我在kernel.request 上创建了一个事件监听器——每个进来的请求,我们循环遍历$_SESSION 中的所有遗留会话变量,并将它们放入 Symfony 的会话包中。这是听众:

namespace AppBundle\Session;

use Symfony\Component\HttpFoundation\Session\Attribute\NamespacedAttributeBag,
    Symfony\Component\EventDispatcher\EventSubscriberInterface,
    Symfony\Component\HttpKernel\Event\GetResponseEvent,
    Symfony\Component\HttpKernel\KernelEvents;

class LegacySessionHandler implements EventSubscriberInterface
{
    /**
     * @var string The name of the bag name containing all the brunel values
     */
    const LEGACY_SESSION_BAG_NAME = 'old_app';

    /**
     * {@inheritdoc}
     */
    public static function getSubscribedEvents()
    {
        return [
            KernelEvents::REQUEST => 'onKernelRequest'
        ];
    }

    /**
     * Transfer all the legacy session variables into a session bag, so $_SESSION['user_id'] will be accessible
     * via $session->getBag('old_app')->get('user_id'). The first bag name is defined as the class constant above
     *
     * @param GetResponseEvent $event
     */
    public function onKernelRequest(GetResponseEvent $event)
    {
        /** There might not be a session, in the case of the profiler / wdt (_profiler, _wdt) **/
        if (!isset($_SESSION))
        {
            return;
        }

        $session = $event->getRequest()->getSession();

        /** Only create the old_app bag if it doesn't already exist **/
        try
        {
            $bag = $session->getBag(self::LEGACY_SESSION_BAG_NAME);
        }
        catch (\InvalidArgumentException $e)
        {
            $bag = new NamespacedAttributeBag(self::LEGACY_SESSION_BAG_NAME);
            $bag->setName(self::LEGACY_SESSION_BAG_NAME);
            $session->registerBag($bag);
        }

        foreach ($_SESSION as $key => $value)
        {
            /** Symfony prefixes default session vars with an underscore thankfully, so ignore these **/
            if (substr($key, 0, 1) === '_' && $key !== self::LEGACY_SESSION_BAG_NAME)
            {
                continue;
            }

            $bag->set($key, $value);
        }
    }
}

如下面的 cmets 所述,这不一定是最好的方法。但它有效。

【讨论】:

  • 后一个选项看起来很简单,有什么缺点吗?如何遍历所有非 symfony 会话值,如果它们在 Symfony 会话中不存在,将它们设置在侦听器中?它是否适用于 n 维数组和标量(所以,基本上是任何值)?
  • 是的,你可以做到。缺点是它使用全局变量更难测试。此外,它总是会触发会话开始。过去我选择了第一个选项,它就像一个魅力!
  • 我最终这样做了,除了可测试性受到影响之外,它似乎还可以:3v4l.org/rZqKs
  • 可测试性是一回事。另一件事是,如果您更改 Symfony 中的值,它们将无法在旧版应用程序中使用。第一个解决方案是双向的。如果对您有帮助,请接受答案;)
  • 我不完全确定这是否可行:Cannot register a bag when the session is already started. (Symfony 3.1.2)。 ` session.legacy:类:AppBundle\Session\LegacySessionHandler 标签:- { 名称:kernel.event_listener,事件:kernel.request,方法:onKernelRequest } `
【解决方案2】:

您的会话变量是否有任何复杂的生命周期?还是在您登录应用程序后它们主要是只读的?

如果后者适用,您也许可以将该“hack”添加到事件订阅者中。这个订阅者会监听Interactive Login Event,它可能看起来像:

class PortSessionHandler implements EventSubscriberInterface
{
    private $session;

    public function __construct(SessionInterface $session) {
        $this->session = $session;
    }

    public static function getSubscribedEvents()
    {
        return [
            SecurityEvents::INTERACTIVE_LOGIN => [
                ['portLegacySession']
            ]
        ];
    }

    public function portLegacySession(InteractiveLoginEvent $event)
    {
        //here you could populate SF2 session with legacy session
        $this->session->set('user_id', $_SESSION['user_id']);
        ...
    }
}

希望对你有帮助

【讨论】:

  • 感谢您的示例,我在 kernel.request 事件上注册了一个侦听器,并遍历所有非 symfony 变量以将它们设置在一个新的参数包中。
猜你喜欢
  • 2018-03-17
  • 2012-10-05
  • 1970-01-01
  • 2015-04-21
  • 1970-01-01
  • 2014-11-26
  • 2015-03-25
  • 1970-01-01
  • 2012-07-20
相关资源
最近更新 更多