A developer friend of mine asked an interesting question[ref]I don’t fault Michael for this question at all. I’ve been in exactly the same boat with a project and asked the exact same question myself. Globals are a messy but very tempting non-solution to several problems that come up in PHP development. I’m often reminded by my team of how much I despise the use of globals because, honestly, they still show up in my code occasionally.[/ref] yesterday on Twitter.
With WordPress, when trying to store instantiated objects in global namespace, should it be in $GLOBALS or $_GLOBALS ?
— Captain Macho Pirate Mick Rackham.yarrđ´ââ ď¸ (@tw2113) September 24, 2014
This question was interesting for a multitude of reasons:
- It’s not that uncommon. Many developers in the WordPress (or even just PHP) space use globals as a way to quickly and easily transport objects from one file or context to another.
- It demonstrates a too-common design pattern that is the largest stumbling block to new developers trying to learn object-oriented programming.
The Rationale
Many developers conflate object oriented programming (OOP) with using classes. Unfortunately, there are many things you can do with a class in PHP that look object-like but are in fact not.
Let’s say you have a set of utility functions you need to use multiple places throughout your code:
@EricMann creating classes that have methods that we end up using in other areas is though. We're just not creating new objects each time
— Captain Macho Pirate Mick Rackham.yarrđ´ââ ď¸ (@tw2113) September 24, 2014
Functions are miniature programs that can live in our out of classes and provide some sort of functionality that is both stateless and independent of where it’s called. Methods are a specific type of functions that belong to an object and act only in the context of that instance of an object.
When working on a complex project, it’s often easier to just drop a utility function into an existing object definition so that function becomes available wherever the object exists. When a project is large, filled with legacy code, or you’re just so far in that a client won’t give you room to refactor (as in this particular case), this is often the easiest way to get what you need done, done.
Unfortunately it also pulls some really bad coding designs into your project and helps to perpetuate a misunderstanding (in the PHP world) of how OOP and functions really work. If you need a function in PHP that behaves in a certain way, it shouldn’t be part of an object at all.[ref]A caveat here is when working with PHP < 5.3. Earlier versions of PHP lack namespacing capabilities, so static classes that represent a collection of statically-defined methods are often used as pseudo namespaces. This is acceptable, so long as you remember that you’re not building objects and thus not doing OOP.[/ref]
Methods on objects should relate to the object on which they’re defined. Static functions wrapped within an object definition should still relate in some way to the object being defined. If you need a truly stand-alone function, leave it as a function and put it in a namespace instead.
One example of how some are doing things:
class My_Stuff {
public function my_function() {
// .. do something awesome
}
}
$Globals['my_stuff'] = new My_Stuff();
// In another file
global $my_stuff;
$my_stuff->my_function();
An example of how things should be done:
namespace My_Stuff;
function my_function() {
// ... do something awesome
}
// In another file
My_Stuff\my_function();
Globals
The “how some are doing things” example creates a standalone instance method rather than a static class function. If the function were static, a developer could easily just call [cci]My_Class::my_function()[/cci] anywhere in their code without issue.
If instead this is a method, then the developer needs a reference to a class instance to call it:
// Either
$my_stuff = new My_Stuff();
// Or
global $my_stuff; // instantiated elsewhere
// Then
$my_stuff->my_function();
If your class is defined in one file, instantiated in another, and the instance method is needed in yet another function (and for some reason you can’t create a second instance of the object), you’ll be tempted to store the original instance in a global variable.
This is a bad idea.
Global variables exist for every part of your application. From the time they’re instantiated to the time the PHP thread invokes [cci]shutdown()[/cci], your object will be sitting there, occupying memory, waiting to be used. One global object isn’t too bad; once you introduce the pattern, though, the global namespace begins to get crowded as more and more objects will be lying around as new developers join the project and copy-paste your design.
Lots of objects in the global namespace equals lots of memory consumption by your application.
Instead, be very careful about what information you actually need at different stages of the application. If you need an object in a function, take that object as a parameter to the function call. If for some reason you can’t add another parameter, create an object factory that can reliably return the object instance you need regardless of where you reference the factory.[ref]Storing a reference in an object factory will still keep it around and use memory. But the factory can also be smart about memory management and recycle the object if needed later. Also, either using a factory or passing an object by reference makes your code more unit-testable as it eliminates hidden dependencies on globals.[/ref]
The short version: global variables are bad, clutter your code, and can be a major stumbling project in terms of project maintainability. Don’t use them.