Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Symfony 2 and custom session variables from legacy application

I'm in the process of setting up the ability to migrate from a Legacy codebase to a Symfony one, and I'm trying to share legacy session variables between the two applications.

I can currently var_dump($_SESSION) in app_dev.php and the user_id key comes through from the legacy application. However, when I run the following in a controller, I don't get any session variables:

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

So to clarify, I currently have access to $_SESSION['user_id'], so the session is shared between applications successfully, but the Symfony session object and it's parameter bags do not contain the legacy keys.

My goal:

  • For the user_id key to be available in the $request->getSession() call
  • For all other keys to be available here

My problems:

I have tried the PHP Session bridge but I don't think this is supposed to do what I want it to. Not only does adding the bridge parameter not do anything in my Symfony application, but from what I've read this is meant to be used somehow in the other legacy application, not in Symfony.

Symfony sessions store data like attributes in special 'Bags' which use a key in the $_SESSION superglobal. This means that a Symfony session cannot access arbitrary keys in $_SESSION that may be set by the legacy application, although all the $_SESSION contents will be saved when the session is saved. [From Integrating with legacy sessions]

I've also taken a look at TheodoEvolutionSessionBundle, but this is old, isn't actively supported or worked on and doesn't work with Symfony 3.

I have noticed that Symfony stores it's session data under $_SESSION['_sf2_attributes'], so my initial thought was to do this in app_dev.php:

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

This is clearly the wrong way to do this, as I also have to call session_start() in there as well.

How can I migrate legacy $_SESSION data into Symfony's Session object? Is there something built-in to Symfony to help with this or a bundle available? If there isn't, where can I place my custom $_SESSION['_sf2_attributes'] 'hack' in the correct place to handle the migration of all these keys?

like image 520
Jimbo Avatar asked Sep 26 '22 06:09

Jimbo


1 Answers

Symfony introduces a concept of session bags into the session, so everything is namespaced. There's no build in solution to access non-namespaced session attributes.

The solution is to use scalar bags, just like TheodoEvolutionSessionBundle does. I wouldn't use it directly, but implement something custom that will work for you project (they only provide integrations for symfony1 and codeigniter anyway). Base it on their idea, but adapt it to your needs.

Alternatively, you could implement a kernel.request listener that would rewrite legacy session attributes to the Symfony one:

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

Jim's edit

I created an event listener on kernel.request - every request that comes in, we loop through all the legacy session vars in $_SESSION and place them in Symfony's session bag. Here's the listener:

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);
        }
    }
}

As explained in the comments below, this isn't necessarily the best way of doing this. But it works.

like image 164
Jakub Zalas Avatar answered Nov 15 '22 13:11

Jakub Zalas