Skip to content

Instantly share code, notes, and snippets.

@markushausammann
Created January 17, 2013 13:59
Show Gist options
  • Save markushausammann/4556064 to your computer and use it in GitHub Desktop.
Save markushausammann/4556064 to your computer and use it in GitHub Desktop.

In this precursor question, I asked about the practical way of using the new Zend\Session component. That question contains a lot of misconceptions I got when first looking at the component.

This question is about the practical way of really doing things.

  • are my proposed ZF ways correct?
  • are there better ways?

For more depth, let's compare how we would do certain things with Zend\Session as well as the Symfony Session component:

Symfony (given you register the namespaced attribute bag):

class Controller {
    private $session;
    public function __construct(Session $session) {
        $this->session = $session;
    }

    //if I want to get a variable from the 'root' namespace
    $this->session->get('key');

    //if I need want to set a variable in a controller specific namespace
    $this->session->set('namespace/key', $variable);

    //if I want to check, if a namespace is set
    $this->session->has('namespace');

    //if I want to add a message to the flash messenger
    $this->session->getFlashBag()->add('info', 'some info');
}

Zend Framework 2:

class Controller {
    private $container; 
    public function __construct(Container $container)
    {
        $this->container = $container;
    }

    //if I want to get a variable from the 'root' namespace. 
    //I guess, I can get the storage first
    //and then check from there, would that work?
    $this->container->getStorage()->offsetGet('key');
    
    //if I need want to set a variable in a controller specific namespace, 
    //that's already the container we injected, so no need to create a namespace
    $this->container->offsetSet('key', $value);

    //if I want to check, if a namespace is set
    //well, it's set because I'm injecting it, so how to verify if it's used... 
    //probably counting elements would do the trick?
    $this->container->getIterator()->count();
    

    //if I want to add a message to the flash messenger 
    //I can use the flashMessenger plugin which is registered automatically, 
    //but it doesn't take care of message type so I have
    //to do that myself, probably by adding an array instead of a message
    $this->flashMessenger()->addMessage(array('type' => 'info', 'message' => 'some info'));
}

It still feels to me like it would be more suitable to inject the storage as session and additionally create a container factory and inject this and then create containers via factory when needed. Whereas with the Symfony Session it seems to completely make sense to inject the session.

@weierophinney
Copy link

I've got a few comments; I'm going to address one thing at a time, though.

First, your last example, with the FlashMessenger: we've never had a concept of message types within the FM; all messages are created equal, and any extra semantics you want you have to provide yourself (as you do in your example). TBH, nobody has ever suggested we provide anything more around this -- until recently: Blanchon Vincent recently create a module called "SuperMessenger" (https://github.com/blanchonvincent/SuperMessenger) which has this concept, and simply builds on the FM implementation; I'm hoping to get him to provide us this functionality in the main project in the future.

@weierophinney
Copy link

I lied; I'm addressing the remaining points in a single comment, as they're somewhat related.

My next point is that you typically either interact with a Container, or you interact with Storage. We have them segregated, because they serve different purposes.

The Storage objects are for interacting directly with the session information; you can think of a Storage object as equivalent, often identical, to $_SESSION. As such, you grab the storage, and you interact with it as you would an array:

$storage = $manager->getStorage();
$storage['foo'] = 'bar';
if (isset($storage['bar'])) { /* do something */ }

The isset call there is what you would do to see if a particular named key of the session exists, and also potentially to see if a given Container exists (more on that later).The offsetGet and similar methods are part of the ArrayAccess implementation, and you generally won't call on them directly; you would call on them indirectly via array notation, as I did above.

A Container is a metaobject. It provides not just access to a segment of the storage, but also provides functionality around expiry of that specific segment. This allows you to have a long-living session with individual bits of it that expire after one hop, or after a shorter duration. When you create a Container, you provide the name, and if that segment does not exist in the Storage, it creates it; hence, after you create a Container, isset($storage[$containerName]) will return true. If you want to see if there's anything in the container, use count():

if (count($container)) { /* ... */ }

So, what you need to do when using sessions is ask yourself the following questions:

  • Am I creating/consuming flash messages? If so, just use the FlashMessenger (or SuperMessenger).
  • Do I want the data I set to have a different expiry than the main session -- i.e., should it expire after a certain number of hops, or within a certain number of seconds? If so, use a Container.
  • Am I essentially just testing, retrieving, or setting session data? If so, use the Storage object.

If you'll be doing more than one of those tasks inside the same controller, ask yourself, "why?" I find I rarely need more than one of those paradigms for a given workflow. If I do, I see if I can break my controller into multiple controllers, so that the workflows are self-contained and only interact with specific bits of the session. (I do this for a variety of other reasons, too -- when I want to break functionality into read vs. write, etc.)

@markushausammann
Copy link
Author

I only see that you answered this here today. Thanks a lot. I guess I'll be editing some of it into your SO answer.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment