How to solve constructor injection of Zend\Session?
Asked Answered
O

1

10

The architecture of the Session component in Zend Framework 2 is yet undocumented and I'm having some trouble understanding it's practical use (compared to the very intuitive Symfony Session for example).

A short summary of the important pieces:

  • Zend\Session\Storage\SessionStorage maps and replaces the $_SESSION superglobal
  • Zend\Session\SessionManager is a facade to manage the storage, the session cookies, session configuration, session validation, etc.
  • Zend\Session\Container is a sort of replacement for the old Session_Namespace, different Containers share one Manager instance (via a static field).

There is no component that represents a collection of namespaces (containers) and therefore there is no way of using methods like 'issetNamespaceX', 'unsetNamespaceX', etc. Nobody (including the Manager as well as Storage) knows about the containers, whether there are any, and if, how many with what names.

Matthew Weier O'Phinney explained this circumstance as follows:

The Container is a special class for working with isolated segments of the current Storage instance. [...] If anything, a Storage adapter would contain Containers, not the Manager. However, we also want to allow more basic usage of storage, which makes the Container orthogonal to Storage, and which explains the difference in has-a relations.

I see a couple of practical problems with this solution with regard to proper dependency injection. Obviously the Manager can be seen as a service with a rather long lifetime and therefore qualifies for constructor injection. Unfortunately, the Manager has no clue about the Containers which forces me to either inject Containers as well (bad because rather short lived and takes slots away), write my own additional functionality to make the Storage or the Manager Container-aware (should be framework functionality) or create Containers in my Consuming classes (which I want to avoid obviously).

So the Zend solution doesn't seem practical to me. If I want to use the Manager, the FlashMessenger and an additional container, I need to inject 4 (four!) classes. If I do the same with the Symfony Session, I only need to inject 1 (one) class.

Furthermore Containers don't qualify for injection since they're potentially short lived run time objects, which may exist or may not at a given point during script execution. With Symfony Session this is not a problem since the Session is aware of it's bags (containers), with ZF2 this is a problem since the Manager is NOT aware of the Containers.


Main question: How am I supposed to use Zend\Session with Containers in practice?

Additional question: Is there a good reason not to provide real namespace functionality similar to ZF1 or for example similar to the Symfony SessionBag?

Obeah answered 16/12, 2012 at 19:51 Comment(15)
Using containers is quite easy, just instantiate and use them: $cnt = new Container('namespaceName'); $cnt['myData'] = 'someValue'; Do you need to do anything further? // Edit: As far as I understand this, a container represents a key of $_SESSION (if using the default storage).Gaillard
Yes, using containers is easy, it's more the question on how to use them in a fully DI enabled environment.Obeah
And, as I say, how to manage them!Obeah
Currently, I'm using Symfony\Component\HttpFoundation\Session\Session to replace Zend Session in my ZF2 applications, because it's understandable, documented and does exactly what I expect it to do. I'll revisit the topic, when I hear from ZF or the docs are up.Obeah
I might have some answers. Can you elaborate on "Obviously the Manager can be seen as a service with a rather long lifetime and therefore qualifies for constructor injection." and why you want to inject a manager and not a container?Kc
Because containers are a) potentially short lived, and b) there might be any number of them.Obeah
Containers are a bundle of parameters; you can see them as a single "namespace". If a controller needs to store some arbitrary data in a session, the controller is completely unaware of the manager and just expects a container to be injected. Your controller should focus around a single aspect, so 99% chance you have also a container for a single "namespace" of parameters too. "Any number" sounds therefore a bit odd.Kc
A container shouldn't care about the manager and storage of a session, that's a completely different layer and the session (with the manager, or directly) should be configured out the scope of the controller.Kc
Yeah, but since the container is orthogonal to the manager, I still need to inject several objects instead of one. With Symfony Session I can just inject the session and I'm sure I have everything, even a pre-configured flashbag.Obeah
You know the "flashbag" is done in ZF2 with a controller plugin called flashMessenger? It is actually the perfect example why you only need a container and not a manager at all: the plugin expects some container to put the parameters in, the rest is up to you to configure (though the default config is already pretty OK): zf2.readthedocs.org/en/latest/modules/…Kc
Which means I have to inject the flashMessenger too, one MORE dependency... I'm starting to think I didn't write my question very well. You don't seem to understand what I'm trying to say.Obeah
@markus-tharkun truth be told, I read this question a couple of times. I'm surprised by the fact that you seem to have a firm grasp of DI and DP/OOP in general. Which makes it even more confusing that the question feels all over the place. Consider giving an example of what DI you're trying to do on what and why.Varicocele
@Varicocele I rewrote my question. Does it make more sense to you now, what exactly confuses you?Obeah
I'm glad for the upvotes, it seems that at least some people understand what I'm trying to ask.Obeah
Weird that I keep getting upvotes for this question even though nobody understands it :D.Obeah
E
5

I'm not 100% certain I understand what issues you're getting at.

First, the constructor for a Container accepts both the namespace for the container, as well as optionally the Manager instance: $container = new Container('your container namespace here', $manager). You can specify a default manager instance to use as well: Container::setDefaultManager($manager).

Second, a Container represents simply a named array within the Storage instance used. As such, you can test for existence of a container by doing an isset($storage['your container namespace here']) call.

What exact issues are you running up against that the above do not solve? From your description, it sounds like (a) you were not aware that containers have a 1:1 relationship with the storage, and (b) that you can inject a manager into the container instances. If there are additional problems, I'd like to better understand them.

Etrem answered 11/1, 2013 at 14:51 Comment(1)
Ok, so I gave this some more thought and understood more of it. I wrote a follow up question but decided it doesn't qualify as an SO question and made a gist instead, cloud you verify my thoughts, please? gist.github.com/4556064Obeah

© 2022 - 2024 — McMap. All rights reserved.