3

I wanted to create a wrapper for Session and Request so that I don't have to access the PHP superglobals directly. I realized that if I create a wrapper for the superglobals and use them, unit testing my application will be easier as the wrapper class can be mocked.

While trying to create my wrapper class, I researched some sample wrapper classes. Some of them stores the superglobal as a class property at initialization:

class Session
{
    protected $vars;

    public function __construct()
    {
        session_start();

        // POINT OF INTEREST
        // Store the superglobal as a class property
        $this->vars = $_SESSION;
    }    

    public function get($index)
    {
        // POINT OF INTEREST
        // Accesses the class property instead of the superglobal
        return $this->vars[$index];
    }

    public function write($index, $value)
    {
        // Writes both class property and session variable
        $this->vars[$index] = $value;
        $_SESSION[$index] = $value;
    }
}

My question: is there any particular reason why while creating a wrapper class we store the superglobal as the class' property instead of accessing them directly? Contrast the above code with this one:

class Session
{
    public function __construct()
    {
        session_start();
    }

    public function get($index)
    {
        // Accesses the superglobal directly
        return $_SESSION[$index];
    }

    public function write($index, $value)
    {
        // Accesses the superglobal directly
        $_SESSION[$index] = $value;
    }
}

IMO, since the wrapper class would be mocked anyway, why bother storing the superglobals as a class property? Is there a particular reason why so many people do this? Should I store the superglobals as a property in their wrapper instead of accessing it directly?

Thanks for any input.

rickchristie
  • 1,640
  • 1
  • 17
  • 29
  • You could make the first one behave like the second version by just storing a reference on $_SESSION into ->$vars. – mario Apr 26 '11 at 20:08
  • @mario - yes but why bother saving it as the class property at all? Is there some sort of value to it or is it over-engineering? – rickchristie Apr 26 '11 at 20:10
  • both of it. Please see my answer. – gd1 Apr 26 '11 at 20:13
  • 2
    Apart from the interceptability I'm not sure what the purpose here is. People would copy them into an attribute to later unset the original superglobals and prevent direct access. ([I know](http://sourceforge.net/p/php7framework/svn/60/tree/trunk/php7/input.php?force=True)). But as Giacomo said, the main advantage of that approach would be reusing the class for both $_SESSION and $_REQUEST. – mario Apr 26 '11 at 20:14

2 Answers2

5

Session is a pretty specific case. But you asked if there was any reason to wrap super-globals. Here are a few possible reasons (Not in order, nor complete):

  1. To make the code less dependent upon global state and hence easier to test. You can test code that depends on a global state. But it's harder and more fragile than testing code that gets told its state.

  2. To make code more flexible since you can then fake requests and sub-requests to do interesting things that wouldn't be possible with a true global state.

  3. To make the code more portable. By wrapping it in a wrapper, you can handle platform dependent things such as stripping quotes, handling character set conversions, etc in a central location. This can make it easier to handle a shift between platforms or multiple platforms.

  4. To enforce additional constraints upon the variable. Since $_SESSION allows you to set anything you want inside itself, you can wind up with a non-serializable state which could cause you problem. With a wrapper, you have one centralized point where you can check the state to determine if it matches the constraints necessary.

  5. To make your code more readable. Sure, just about every php developer knows what you're doing if you access $_POST in a method. But do they need to know about that implementation detail? Or is $request->getFromPostData('foo'); more verbose?

  6. To make your code easier to debug since you can set a breakpoint in the request class and immediately find all occurrences of accessing request variables (as long as you never access them directly).

  7. To make your class's dependencies easier to understand. If I gave you an API for a class that used the super globals, you can't tell if that class accessed it, and hence exactly what the class needs to operate. But if you require a request class to be injected, you could tell at a quick glance that the class actually does need something from the request to operate. Hence improving readability and clarifying your API more.

There are more reasons in reality, but those are the ones I can think of off the top of my head.

ircmaxell
  • 163,128
  • 34
  • 264
  • 314
  • Thanks for the write-up, would bookmark this to show to my friends when they scoff at me using wrappers :) - Is there a specific reason why many superglobal wrappers copy the superglobal as class property instead of accessing them directly (the difference between the first `write()` method and the second one, which one is better?) – rickchristie Apr 26 '11 at 20:48
  • 1
    I wouldn't copy anything. I'd pass it in as a constructor argument. That way, you're still as flexible as possible... – ircmaxell Apr 26 '11 at 20:58
  • That's how I've done my request wrappers in the past, but they are with the read only superglobals. Especially useful when you have to deal with [`apache_request_headers()`](http://us3.php.net/manual/en/function.apache-request-headers.php) and unit tests. – Paul DelRe Apr 26 '11 at 21:53
3

I don't think there's some important reason, maybe it's just that if you change the superglobal you're referring to afterwards, you haven't got to replace it in the whole code of your class, but just in the constructor.

From this point of view, I find not very sensible the write method of the first implementation you provided, that writes both class property and session variable, as you remarked.

All of this seems a bit overkill (and overthink), however. Just keep simple and do things that are useful to you.

gd1
  • 11,300
  • 7
  • 49
  • 88
  • Could you elaborate more on what you mean by *in a second moment*? I don't understand.. – rickchristie Apr 26 '11 at 20:15
  • For example (warning: going into perfectionist over-engineering) you might want to replace `$_SESSION` with some other custom device to store session-like variables, or like it happened between PHP3 and PHP4 with `$HTTP_POST_VARS` and `$_POST`, you can be forced to migrate to a new syntax. – gd1 Apr 26 '11 at 20:20
  • ouch... I discovered "second moment" is not proper English! :D :D Take it as: afterwards – gd1 Apr 26 '11 at 20:32