Page 1 of 1

Session Manager

Posted: Tue Dec 01, 2009 12:14 pm
by JNettles
This is a Session Manager that I came up with to try and manage our sessions more efficiently. It works by separating the session into "pools" (groups) that can be manipulated individually, rather than effecting the session as a whole. My projects frequently involve recycling portions of the session as events occur (I'm using this plugged into my MVC framework, where I believe it has the greatest potential since it allows for stronger event-driven programming).

For example, a user logs in an is registered with the session as "username" but also with a whole host of other necessary data that I store to save making a trip to the database on every page. This data should persist throughout the term of the login and doesn't need to be touched until they log out. I call this the "Application" pool. (You can any number of pools, and of any name so long as they are in legal variable format - each pool is declared as a constant.)

However, there are eight major components to the application, each of which requires some interaction with the session at some time or another. Each component can be assigned its own pool in the session, which I call "Context". A user enters a component and a pool is set aside for handling all session data within that context. More importantly, once they leave that component I can establish a rule that recycles that pool alone, leaving my application pool and any other pools untouched.

Here's the class. I'm hoping some people will have suggestions/criticisms that I can use to make this better! Its already helped us immensely but I'm sure there's stuff I've missed.

Code: Select all

<?php
 
/**
 * This is a more advanced session manager that groups session data into logical pools.
 * Data is grouped into session pools - technically you can have two identical keys
 * that point to different pools. For example if you have the key "user" for the Context session then you can
 * also have a "user" key that points to the Application session.
 * 
 * The idea behind this is to properly manage sessions without a timer or waiting on the session cache to expire.
 * By effectively using the Recycle function, you can create conditions for when certain pools are recycled to separate
 * permanent, temporary, and volatile data.
 * 
 * @author Jarrod Nettles - 2009
 *
 */
class SessionManager
{
    /**
     * Starts the session and retrieves any existing pools.
     * @return void
     */
    public function __construct($session_start = false)
    {
        if($session_start == true)
        {
            session_start();
        } 
        $this->retrievePools();
    }
      
    /**
     * Starts the session, if it hasn't already.
     * @return void
     */
    public function StartSession()
    {
        session_start();
    }
 
    /**
     * Pass a comma delimited string of the pool names you need to create. Creates a constant for you to use
     * when referencing the pool later.
     * @param $pools String
     * @return void
     */
    public function CreatePools($pools)
    {
        //register the available pools in the session so that they can be retrieved at any time
        $_SESSION["session_pools"] =  $pools;
        
        //break the pool list into an array
        $pools = explode("," , $pools);
        
        //create a constant for each pool provided
        foreach($pools as $item)
        {
            define(trim($item), trim($item));
        }
    }
    
    /**
     * Retrieves the existing pools from the session - called on class initialization.
     * @return void
     */
    private function retrievePools()
    {
        if(isset($_SESSION["session_pools"]))
        {
            $this->CreatePools($_SESSION["session_pools"]);
        }
    }
    
    /**
     * Retrieves data from a session pool based upon its key.
     * @param $key String
     * @param $type Constant
     * @return unknown_type
     */
    public function GetData($key, $type)
    {
        return $_SESSION[$type . "_" . $key];
    }
    
    /**
     * Creates or modifies data in the provided session pool.
     * @param $key String
     * @param $value Any
     * @param $type Constant
     * @return void
     */
    public function ModifyData($key, $value, $type)
    {
        $_SESSION[$type . "_" . $key] = $value;
    }
    
    
    /**
     * Empties an entire session pool.
     * @param $type Constant
     * @return boolean
     */
    public function Recycle($type)
    {
        try
        {
            foreach($_SESSION as $key => $value)
            {
                $check = explode("_", $key);
                if($check[0] == $type)
                {
                    unset($_SESSION[$key]);
                }
            }
            
            return true;
        }
        catch(Exception $e)
        {
            return false;
        }
    }
    
}
Example.

Code: Select all

<?php
 
$sm = new SessionManager(true);
 
$sm->CreatePools("Application, Context");
 
    $sm->ModifyData("username", "JNettles", Application);
    $sm->ModifyData("email", "JNettles@company.com", Application);
    $sm->ModifyData("ACL", 5 , Application);
 
    $sm->ModifyData("username", "Jarrod", Context);
    $sm->ModifyData("contact_id", 5476880, Context);
    $sm->ModifyData("application_id", 43454748, Context);
    $sm->ModifyData("return_page", "articulations", Context);
 
echo $sm->GetData("username", Application);  //echos "JNettles"
echo $sm->GetData("username", Context);      //echos "Jarrod"
 
//recycle our Context pool (unsets the entire pool)
$sm->Recycle(Context);
 
//Application pool is untouched
echo $sm->GetData("ACL", Application);  //echos "5"
 
?>
 

Re: Session Manager

Posted: Tue Dec 01, 2009 1:30 pm
by Christopher
Rather than "pools" I think a straightforward namespace system would be clearer. And I like to have lazy start as a basic feature so you can create the session manager object, but not start the session until it is actually used.

Re: Session Manager

Posted: Tue Dec 01, 2009 2:04 pm
by JNettles
Thanks for the session_start suggestion. I modified the construct so that you have to toss a "true" argument to automatically start the session.

What sort of style were you thinking for namespaces? I think I have a fairly solid method of grouping session data but if you have a better suggestion for syntax.....?

Re: Session Manager

Posted: Tue Dec 01, 2009 2:52 pm
by John Cartwright
Generally, you each session namespace would be contained within it's own object.

Code: Select all

$session1 = new Northern_Session('my_namespace');
$session1->foo = 'bar';
 
$session2 = new Northern_Session('my_other_namespace');
$session2->fee = 'bar';
I also find it easier to implement __isset(), __get(), __set(), and __unset() in my data access objects instead of methods, because they are simply data containers and it far less characters to type.

Re: Session Manager

Posted: Tue Dec 01, 2009 3:44 pm
by JNettles
I like your idea John - the only problem I see with it is that now I have two objects instead of running everything through a single instantiation. In fact, at certain points I have up to five pools in the session at one time - doing $session1.... $session5 gets somewhat unwieldy trying to remember those variable names, not to mention the fact that I would have 5x the memory usage in my script. Yours would be alright (and probably more convenient) in a smaller project but in our framework we need the readability of a single class (its now the standard that all session references go through a variable called $sm).

I agree though, mine definitely is more typing. :wink: Could I see the code to your class?

Re: Session Manager

Posted: Tue Dec 01, 2009 4:01 pm
by John Cartwright
JNettles wrote:I like your idea John - the only problem I see with it is that now I have two objects instead of running everything through a single instantiation.
There is nothing wrong with creating more objects, in fact, it is recommended you group your data through multiple objects instead of a monolithic super class.
JNettles wrote:In fact, at certain points I have up to five pools in the session at one time - doing $session1.... $session5 gets somewhat unwieldy trying to remember those variable names, not to mention the fact that I would have 5x the memory usage in my script. Yours would be alright (and probably more convenient) in a smaller project but in our framework we need the readability of a single class (its now the standard that all session references go through a variable called $sm).
If you are concerned about the memory usage of creating a few extra objects, then PHP, nay, OOP, is not for you! Pre-optimization is the root of all evil. I would argue that this method is far more readable and understandable since the objects are compartmentalized by common data. Abstracting these groups into a some kind of registry is only complicating matters without any benefit. It is pointless to create a registry of super globals, plus it introduces some ugly defines (which seem completely out of context).

As for the naming conversion, I wouldn't actually recommend you use $session1, $session2, as variable names. Perhaps something more descriptive such as:

$contextsession = new ...
$applicationsession = new ...
JNettles wrote:Could I see the code to your class?
Sorry, not opensource. However, it is very similiar to Zend_Session component in the Zend Framework.

Re: Session Manager

Posted: Tue Dec 01, 2009 4:29 pm
by JNettles
I can see the benefits of namespacing like arborint showed and what you've said - its a question of grouping: do we group based upon when they'll be recycled versus what context they exist in. But I don't always need to recycle solely on group - actually, very rarely - rather, like I posted above, the groups are recycled based upon which part of the site they are navigating to and if all of my data about "user" is aggregated into one "user" group then I'm potentially left with extraneous data in "user" from a completely different section of the site. Whereas if I group by recycle my groups stay very concise and specific in what they are maintaining.

And, specifying the groups this way lets me have very strict control over how the sessions are recycled. In our application, after a user supplies login information we instantiate the session manager and create three pools, Application, Context, and Volatile. Application contains data that is specific to the entire application and will perpetuate through the user's duration. Context is for specific parts of the site, and any time they browse away (ie: click on a menu item) the Context pool gets recycled when the page request comes through the master controller. If we grouped by description instead of recycle we could have any number of session pools created by a programmer (that don't necessarily conform to a standard) that wouldn't get recycled until the session expired. This way, we know that there are three predefined pools and we know exactly when each pool will be recycled.


This is hardly a registry class - all it does is handle incoming and outgoing session requests by modifying the session key to reflect each item's particular group. And the defines are optional, for clarity only - it works the same even if it you pass it a string every time.

Re: Session Manager

Posted: Tue Dec 01, 2009 4:42 pm
by John Cartwright
What do you mean by recycle? I don't see how you are gaining any further fine grained control over your sessions by doing it your way versus this way. Both methods generally group common data together, however, you represent all your sessions in an array contained by a monolithic object, whereas my methods represents each session namespace as an object.

Regardless, you seem happy with your method. I won't try and convince you further. Where we differ is you believe in containing your entire session within a single object, which means you have to pass the entire object (related data and otherwise unrelated) to gain access to your session. Whereas my method you can give access to exactly what is needed. The components that require specific session data to receive/modify should not be aware of what context the data is in.

P.S., I called it a registry because multiple unrelated datum may be registered to your object. So yes, technically it is a registry.

Re: Session Manager

Posted: Tue Dec 01, 2009 10:13 pm
by JNettles
I'm not trying to be closed-minded about this, I'm really just trying to understand your viewpoint here, because I don't think you've actually looked at how my class works. You keep mentioning mine being stored in an array but there isn't an array anywhere present in the class. There's no array (or object) being dumped into the session, and it certainly doesn't return the entire session when requesting data.

I'll map it out.

Create two pools -> "Application", "Context".

Add an item to the session -> username = "JNettles", "Application"

The class takes the supplied key of username and the supplied pool name of Application and combines them, so the session key becomes Application_username. Then when pulling out the session, you supply a key and a pool, and so the class knows to request POOL_KEY from the session. The only overhead I add to the session at all are slightly longer key names than normal.

It would help I knew a little bit better how you store your information in the session. Are you serializing an entire object into the session?

p.s. -> When i say recycle I mean to unset every variable in the specific pool. Sorry, I try to keep company-specific lingo off my posts. :wink:

Re: Session Manager

Posted: Tue Dec 01, 2009 11:10 pm
by John Cartwright
I looked at your code carefully, and understand your design decisions.
You keep mentioning mine being stored in an array but there isn't an array anywhere present in the class. There's no array (or object) being dumped into the session, and it certainly doesn't return the entire session when requesting data.
$_SESSION is an array, although that's not what I meant. Essentially both of our methods will use an array to store the data. However, what I wanted to convey was your $_SESSION array contained multiple keys (or pools). Mine method will only store the key pertaining to it's specific namespace. The difference is when we pass data around, you are passing the ENTIRE session, versus only the relevant pools.
The class takes the supplied key of username and the supplied pool name of Application and combines them, so the session key becomes Application_username.
Why not store it as $session['Application']['username']? Groups the data in a more logical way, and is more convinient when checking for specific elements. It will also allow you to return an entire "contexts"'s variables instead of only a specific element.
Then when pulling out the session, you supply a key and a pool, and so the class knows to request POOL_KEY from the session. The only overhead I add to the session at all are slightly longer key names than normal.
I'm not sure why overhead is even being considered as a topic here :D We are talking microseconds difference. But to the point, the fundamental different between our view points is you have to specify the context of the variable you want to retrieve. I believe the interfaces should be explicit when possible, and passing a session object with information only pertaining to the specific method makes things more clear. When we pass the "session manager" around to other objects, these objects need to be aware of the specific context in which they need to retrieve the data, and thus creates a tight coupling between the two objects. I won't go into why tight coupling is not ideal, but simply put it eliminates portability, making testing harder (mocking), etc.

Now, by creating new objects for each session namespace, we can tell exactly what data we are working with without specifying a context. We can also now subclass and extend the functionality of this specific set of session values without affecting other "pools" variables.
It would help I knew a little bit better how you store your information in the session. Are you serializing an entire object into the session?
You can put anything you want into the session, and if it happens to be an object then yes it will be serialized to maintain persistence. The serialized objects will then implement __sleep() and __wakeup() to handle any initialization or shutdown functionality.
p.s. -> When i say recycle I mean to unset every variable in the specific pool. Sorry, I try to keep company-specific lingo off my posts. :wink:
This would be much easier with grouping your session data in by their pool key (as mentioned at top of my post). However, fundamentally, deleting an entire "pool" or just as easy for both our methods.

Re: Session Manager

Posted: Wed Dec 02, 2009 12:27 am
by JNettles
Ahhh I got you now - that was a lot clearer. Thanks, I'll rework the class a bit to take in some of the suggestions.