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.
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: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).TheoffsetGet
and similar methods are part of theArrayAccess
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, usecount()
:So, what you need to do when using sessions is ask yourself the following questions:
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.)