Gerrymandered Code: Extending Zend_Registry to globalize session vars

In my most recent post I talked about putting my toes in the Zend Framework water with stand-alone usage of Zend_Cache. This has been a great experience, and since writing the post I’ve been updating my main application with more caching anywhere I can see a benefit.

In the past couple of days I waded a little deeper into ZF, installing Zend_Registry (also stand-alone) and using it to globalize configuration variables that I was previously loading into $_SESSION in order to access throughout my scripts and inside classes without having to pass them explicitly.

I’ve been using $_SESSION like this for a couple of years, and I’ve known the whole time it was a band-aid approach to solve the problem. Such an approach is not an ideal “design pattern.” Though it passes my baseline rule–it works–there are downsides. The two most obvious are that 1) there’s nothing to stop me from accidentally overwriting a session var inside some script or method, and 2) there’s no sense loading up the session handler with stuff that really shouldn’t be in there.

Zend_Registry is a very lightweight module (one file), that is actually an extension to the native ArrayObject PHP5 class. Any variable, object, or scalar you load into Zend_Registry instantly becomes available globally throughout your script. Let me prevent confusion by saying there is no persistence across page loads–this is just for within each request–but the upside is any scope issues are swept away in a very elegant manner. Hint: Combine Zend_Registry with Zend_Cache to achieve persistence.

The Registry design pattern is not unique to ZF, you can even write your own Registry class if you care to.  I’m still not fluent in OOP-speak, but I understand Zend_Registry is implemented by default as a Singleton.  I know there’s a lot of trashing of the Singleton pattern out there in programmer land.  However, I don’t get too hung up on such metaphysical debates…not to say there isn’t merit in the argument, but for me and my work it is not a practical concern.

I refactoring my main app with Zend_Registry in place of my rat’s nest of $_SESSION[‘config’][‘foo’] and such…which is mostly just things like email addresses for the system admin (me) and other global variables that may come in handy in any script or method.

As with my previous ZF article, I’ll leave the details to the fine folks at Zend (see Zend_Registry documentation here), but the implementation of Zend_Registry in this fashion can be exactly this simple:

Zend_Registry::set('admin_email', 'chris.renner@company.net ' );

And then later in your script (or function or object method or wherever) you return the value by calling:

Zend_Registry::get('admin_email' );

Wow, how elegant! You can also access the registry as an object or as an array. For my needs, the static setter/getter methods are perfect.

Netbeans’ Find and Replace features made it relatively simple to update my code with the new Zend_Registry calls.  I guess the “web developer” jargon for that would be “I refactored my code base.”

Problem solved, right?  Well yes and no.  The pesky need for globalized config vars has been solved, but there is a related problem.  How did I ever get the idea to store config variables in $_SESSION?  Probably the same way most of you reading this did in your newbie days:  I was already using $_SESSION to store actual, legitimate session variables at user login, and then access those variables through my code.

gerrymanderIn addition to being a code jockey, I am a politics junkee and also the owner of a second major in Political Science (I know, its so rare to find someone working in a field that is wholly unrelated to their degree…).  It occurred to me there is an analogy to be made between a  Gerrymandered congressional district and what the pros call “spaghetti code.”  I think my amateur-ish use of $_SESSION as a global band-aid amounts to what one might call “Gerrymandered code.”  I am hereby coining the phrase!

The obvious solution to replace all the $_SESSION tags littered throughout my code is to simply load $_SESSION into Zend Registry like so:

Zend_Registry::set('session', $_SESSION);

This works, but retrieving session vars proves to be a bit clunky. My reading of the ZF documentation suggests there isn’t a way to access the value of an array element in the Registry in a direct fashion. You therefore have to instantiate Zend_Registry every time you need access to a session var. For example…

$registry = new Zend_Registry();
echo $registry['session']['userID'];

I find this a bit clunky to do every time I need to access my session. I also don’t care for all the extra overhead caused by generating an object for each one of these cases.

My solution was to extend Zend_Registry with another class that can access an array element inside the registry and return it directly. Here’s what I came up with:

/**
 * A simple extension to Zend_Registry to allow direct access to array objects
 * and object properties inside the registry
 * @author Chris Renner http://www.chrisrenner.com
 */
class App_Registry extends Zend_Registry {
 
    /**
     * Constructs a parent Zend_Registry with default
     * ARRAY_AS_PROPS to allow access as an object
     *
     * @param array $array data array
     * @param integer $flags ArrayObject flags
     */
    public function __construct($array = array(), $flags = parent::ARRAY_AS_PROPS) {
        parent::__construct($array, $flags);
    }
 
    /**
     * getter method for directly accessing value of an array element stored in a registry index
     *
     * @param string $index - the array we're accessing
     * @param string $key - the element we want to access
     * @return mixed
     */
    public static function getFromArray($index, $key) {
 
        $array = self::get($index);
 
        if(is_object($array)) {
            $var = $array->$key;
        } elseif(is_array($array)) {
            $var = $array[$key];
        }
 
        return $var;
    }
 
    /**
     * setter method for directly modifying value of an array element stored in a registry index
     *
     * @param string $index - the array we're accessing
     * @param string $key - the element we want to access
     * @param string $val - the value we want to assign
     * @return mixed
     */
    public static function setInArray($index, $key, $val) {
 
        $registry = self::getInstance();
 
        if(is_object($registry[$index])) {
            $registry[$index]->$key = $val;
        } elseif(is_array($registry[$index])) {
            $registry[$index][$key] = $val;
        }
 
    }
 
}

As you can see, there are just two methods. getFromArray returns the value of an array element or object property from the registry. You just need to supply the Zend_Registry index and the key/property name you want back.

setInArray is used to update an existing property or array element. Supply the Zend_Registry index, the property/key name, and the value you want to assign.

So now, if I want to get the userID from the session (We still set the session array into the registry using Zend_Registry::set() as above), I can call a new static method like this:

echo App_Registry::getFromArray('session', 'userID');

…and to change the value I simply call this:

App_Registry::setInArray('session', 'userID', $newValue);

Obviously this is a very rough class.  It could use more polish, like error handling in case we’re trying to access something that is neither an object or an array.  This can be used to access elements/properties for any array or object you might pass into the registry (not just session vars).

You might ask why not just use constants to globalize config vars?  You certainly could in a simple application.  If you want to change those vars in the script or store an array, you’re out of luck.

I’m curious to see what readers of this post might think, if you have suggestions for improvements or better ways of doing the same thing.  Thanks for reading!