PHP globals for the OOP developer

Update: I evidently simplified the issue too much, and have had several people rightly comment on the bogosity of the issue. However, there are still situations where $GLOBALS does not act as expected, and I outline these in my next entry.


In my previous entry, I ranted about the use of globals in popular PHP applications, and how they make embedding said applications difficult. I develop using object-oriented practices, and can honestly say I can't recall ever having slung a global variable around in my own code. Globals seem hackish to me, and as a result, trying to get applications that use them to behave correctly has been a challenge.

One of the applications I had in mind was Serendipity, the software that powers this blog. I was attempting to create a Zend Framework action controller that wraps my s9y instance so that I can do things such as apply ACLs from my website to selected entries, as well as pull the sitewide skeleton out from s9y so that I only have to maintain one version of it (I had one version for s9y, and another for my own content featured on the site (resume, contact form, etc.).

I tried importing the various config files into my action method prior to invoking the actual s9y bootstrap, but no dice. I also tried modifying the s9y config files to use the notation $GLOBALS['serendipity'] around the serendipity configuration variables (s9y uses a single multi-dimensional array for all configuration options). This didn't work, either; s9y functions that called global $serendipity were still getting a null value.

So, I did a little closer reading in the manual section on predefined variables, I discovered something interesting in the description of $GLOBALS (emphasis mine):

Contains a reference to every variable which is currently available within the global scope of the script.

Interestingly, the section on variable scope didn't make this distinction at all. Basically, if the variable you reference via $GLOBALS does not already exist, assigning it does nothing. It doesn't even raise a notice. It just silently goes ahead, leaving you thinking you set a new global variable, but in fact, you cannot assign new globals via $GLOBALS; you can only modify existing variables in the global scope.

So, I got around the issue by putting this in my front controller bootstrap:

$serendipity = null;

After that, I was able to create a wrapper action controller for s9y very easily:

/** Zend_Controller_Action */
require_once 'Zend/Controller/Action.php';

/**
 * Serendipity integration
 * 
 * @uses       Zend_Controller_Action
  */
class S9y_IndexController extends Zend_Controller_Action
{
    public function init()
    {
        // New ViewRenderer helper in ZF incubator; telling it not
        // to autorender a view script when done
        $this->_helper->viewRenderer->initView(null, null, array('noRender' => true));
    }

    public function indexAction()
    {
        global $serendipity;
        chdir($_SERVER['DOCUMENT_ROOT'] . '/path/to/s9y');
        include './index.php';
        chdir($_SERVER['DOCUMENT_ROOT']);
    }
}

Note that I don't do any output buffering; this is because the ZF dispatcher takes care of that for me. All I need to do is execute the s9y bootstrap.

So, the lesson to learn from all this: if you need to wrap an application that uses globals, find out what all of them are, and declare them in the global namespace — just setting them to null is enough — in your application bootstrap.