What's Wrong With (PHP) Globals
When I've contributed to PHP projects with fewer global variables, I've noticed that the flexibility and maintainability of the code has been significantly greater, and I have more fun working with it. My theory: if there are no globals, we can predict that an object doesn't play with any toys that we haven't given it.
With globals, the protocol for usage of an object could be complicated and implicit. Given the class:
<?php
class Foo {
function Foo($number) { ... }
function doBar($number) { ... }
}
?>I might reasonably expect the protocol for using this object to be:
<?php
$foo = new Foo(12);
echo $foo->doBar(47);
?>when in fact, the protocol could be:
<?php
$GLOBALS['calculator_helper'] = new CalculatorHelper("pgsql://localhost/db_cache");
$_SESSION['state'] = 'initializing';
$foo = new Foo(12);
$_SESSION['state'] = 'calculating';
echo $foo->doBar(47);
?>If I were to remove global references, I'd rewrite the interface like this:
<?php
class Foo {
/**
* @param CalculatorHelper:: $helper
*/
function Foo(&$helper, $number) { ... }
function setState($name) { ... }
function bar($number) { ... }
}
?>While not perfect, I'm comfortable that this better communicates the object's protocol.
So, a no-globals rule means:
- I can reuse objects more confidently in different contexts, since I can trust that it needs only what it explicitly asks for by way of constructor and method call signatures.
- I can easily guess what an object depends on by looking at its usage.
- I can more easily find method calls and constructions of a class, for adding parameters and refactoring and such.
Consider What Global?
Exercising deft circular reasoning, if removing globals makes explicit which toys an object can play with, we can define a "global" as anything that an object can play with which was not explicitly handed to it.
For these purposes, then, along with variables declared global I consider PHP's autoglobals ($_SESSION, $_REQUEST, $_COOKIES, etc.) to be global, as well as static variables in functions and methods. This last point requires us to bring in the "Singleton" pattern as a global.
Global state also hides inside some PHP library functions: ini_get() and ini_set() are obvious, but also consider the output buffering functions. Granted, these don't usually get in the way, since the ini functions are typically used only during initialization and the output buffering functions hide a stack and beg to be used in a balanced fasion within the calling functions—but so long as I am fleshing out the terrain, let's not avoid the corners.
If it seems like "global temptations" lurk everywhere in PHP, it is because they do. Unlike Ruby, where globals are rarely seen and introduced by the scary "$", PHP requires the poor coder to use autoglobals to interact with the person at the browser. The result is that the uncareful developer winds up testing whether $_REQUEST['flag'] is set smack in the middle of his model code—and the unit test will catch that sort of error approximately once every never.
What To Do?
- Do not use the global keyword ever, unless you plainly cannot help it. This goes for the $GLOBALS autoglobal as well.
- Localize access to autoglobals like $_SERVER and $_SESSION. I recommend wrapping them in classes anyhow, so that you have another target for the almighty Move Method refactoring.
- Avoid the Singleton pattern. It seems a lot of people react strongly to this—"But, this is a Design Patterns pattern!" Yes, yes. I know. I was lulled into its comfortable sense of problem-solvingness for a period of time. So I'll give you two options. Either avoid "Singleton" like the dickens, or buy a really large bottle of buffered analgesic to keep on hand when trying to code your unit tests.
Refactoring
Since I am currently advocating practices to remove the use of global state from the Horde Project, I will be posting some articles on small practices (patterns and refactorings) which can be easily ingrained in Open Source contributors. In other words, more to come!


Mmmmmm....Globals
An excellent post, Jason. You wrote, "If it seems like 'global temptations' lurk everywhere in PHP, it is because they do." Having spent much time programming in pre-.NET Visual Basic, I can relate! PHP, of course, gets a lot of flack for this sort of thing (allowing people to get themselves into trouble, that is), just as Visual Basic always did, and it takes constant effort to promote good practice.
Welcome to developer.*. I look forward to more posts.
Dan