When I build a WordPress plugin, I use several methods to structure things that help me greatly, all of which stem from root composition. I’m going to expand on what that is and how I use it.
Every plugin has a root file that contains the plugin header comment, and gets loaded by WordPress. This is the entry point into your plugin, the starting line for your code. A lot of plugins are a single file, and do most of their heavy lifting in in the root plugin file, but this puts everything all together in a mess that sends developers scrolling up and down looking for things, which gives me the first rule:
Don’t do work in the root plugin file.
This puts all our logic in external files, so we need to include those files if we want to do things. Now the ordering of those files becomes important, and we can optionally load files based on conditionals, e.g. don’t load the admin interface file if we’re on the frontend. The main file becomes a lot cleaner now, and starts to revolve around setting things up to do work, rather than actually doing that work.
That’s still logic though, and if we want files loaded in a different order in one scenario, but not another it gets complicated. That leads us to the second rule:
Code shouldn’t do work until the programmer says so
I do this by using objects, I build the object in the constructor, but I start the logic in a method called run
. This way I can build my plugin, then I can run it if I want to. I’ve separated construction and operation from each other. My run method is normally filled with calls to add_action
and add_filter
.
If I buy a washing machine, I don’t expect all my clothes to immediately fly into the door and come out clean. Buying and installing a washing machine is not the same as using and running a washing machine. Set it up once, and wait until it’s needed, then run. Otherwise I’d have to buy a washing machine every time I wanted to wash jeans
This separation means I now have a period where I can treat my code and objects like lego bricks. I can assemble things how I like them, and when I’m finished I press start, and off they go doing work.
That still leaves us with a problem, all our objects have go buttons. We don’t want to panic and press 100 buttons at once to get things going, which order do we want to press the buttons in? What if I press them in different orders and introduce bugs? This leads us to the next rule:
Every thing has to live somewhere
In my plugins, I try to create an object for each task or responsibility, e.g. an object that registers my stylesheets, or an object that implements a post type, but those objects live somewhere in my plugin. Instead of making them homeless I create dedicated control objects. These controllers do very little, they had a constructor and a run method. The constructor takes a list of objects that are going to live there, and the run method passes along the “it’s time to start working” message, e.g.
class Kitchen { $microwave = null; $kettle = null; public __construct( MicroWave $microwave, Kettle $kettle ) { $this->microwave = $microwave; $this->kettle = $kettle; } public function run() { $this->kettle->run(); $this->microwave->run(); } }
Here I can assemble my kitchen, and it immediately tells me I need a microwave and a kettle to build a Kitchen. Once I’ve done that I can tell it to run:
<?php /** * Plugin Name: Kitchen Example */ // tell PHP what a Kitchen is require_once('microwave.php'); require_once('kettle.php'); require_once('kitchen.php'); // build the kitchen $microwave = new MicroWave(); $kettle = new Kettle(); $kitchen = new Kitchen( $microwave, $kettle ); // and tell it to do stuff $kitchen->run();
That Ladies and Gentleman, is root composition. You may wish to separate out your PHP files into a php or includes folder, that’s up to you.
If you need to use __FILE__
, pass it in from your root file, just like a Kitchen says it needs a Microwave, your plugin should say it needs a __FILE__
. Don’t be afraid to make your smaller building blocks demand things. For example, my Kettle object might declare that it needs a Water object in its constructor, so I give water to the kettle, not the kitchen:
// How could I have forgotten, Kettles boil water! Lets put water in our Kettle $water = new Water(); $kettle = new Kettle( $water );
Root composition also prevents a lot of issues that can appear involving initialisation and timing. Code can run safely in the knowledge that everything has been built beforehand, and doesn’t have to check if we installed the microwave yet, or if the Kettle has water when we boil it. It also makes testing a lot easier as you can swap out objects for test versions that ‘pretend’ to do work.
This is all thanks to root composition giving us a code life cycle:
- We define our classes and load our files so PHP knows about our plugin
- We build our objects so they’re ready to use
- Finally we press the start button, and our plugin starts running
There’s several ways of implementing each stage. For example, step 1 could be loading an autoloader if you’re using Composer. Step 2 could use Object factories and service containers to build your objects. Step 3 could be a PHPUnit test. They all have their advantages and disadvantages but having a structured life cycle allows you to plan ahead.
For example, a developer could insert a step 0 before loading to check if the user is running a supported version of PHP, aborting if the version isn’t high enough.
If you found this useful, you may find dependency injection interesting, as well as separation of concerns and Demeters law. I like root composition because it makes good OO programming easier, and encourages structured code
RT @Tarendai: Root Composition in WordPress Plugins http://t.co/vjVZ0TX6qC #wordpress
RT @Tarendai: Root Composition in WordPress Plugins http://t.co/vjVZ0TX6qC #wordpress
RT @Tarendai: Root Composition in WordPress Plugins http://t.co/vjVZ0TX6qC #wordpress
RT @Tarendai: Root Composition in WordPress Plugins http://t.co/vjVZ0TX6qC #wordpress
RT @Tarendai: Root Composition in WordPress Plugins http://t.co/vjVZ0TX6qC #wordpress
RT @Tarendai: Root Composition in WordPress Plugins http://t.co/vjVZ0TX6qC #wordpress
Pingback: RT @Tarendai: Root Composition in WordPress Plugin… | Jtsternberg Tweets
RT @Tarendai: Root Composition in WordPress Plugins http://t.co/vjVZ0TX6qC #wordpress
RT @Tarendai: Root Composition in WordPress Plugins http://t.co/vjVZ0TX6qC #wordpress
Nice article. Just two things: from your article seems that autoload requires Composer, of course that’s not true. Second (most important) thing: root composition is pretty broken without interfaces. Kitchen should be able to work with any MicroWaveInterface and KettleInterface object. If your *real* microwave is broken, you can buy a different brand & model, you’ll still be able to cook stuff.
@Tarendai are Automatticians authorized to use Interfaces in their code? 😛
RT @Tarendai: Root Composition in WordPress Plugins http://t.co/vjVZ0TX6qC #wordpress
What is your thought about people initializing class inside the class file? My opinion it is a bad practice! A class file should contain only the class. It shouldn’t do anything and initialization should be from another file.
I have seen lots of plugin in WP repository does this and as a result beginners picking up this habit :-/
Most probably you’ve seen the Singleton design pattern which is ok.
The Singleton design pattern is bad, as it represents global state and makes it impossible to do unit tests.
In WP it’s even worse as developers use it to avoid structuring their code. You don’t need to think about where an object lives if you can be lazy and call
$obj = MyObject::instance()
all over your codeI agree, loading a file with a class should make that class available to you, but testing that class is a problem if it runs anytime its file is loaded. For the same reason, constructing your application is difficult when this happens as you no longer have control over execution
Nice article.
Pingback: Creating a Custom CMB2 Link Picker Control for WordPress - Wholesome Code
Pingback: Creating a Custom CMB2 Link Picker Control for WordPress - Matt Watson
Pingback: Creating a Custom CMB2 Link Picker Control for WordPress – wholesomecode.net