One of the biggest things that makes WordPress so popular is its extend-ability. Just by running add_action
or add_filter
on the right hook, you can extend just about anything, both in core and in WordPress plugins. By running register_post_type
, you can add a new post type. You can enqueue any stylesheet or script using wp_enqueue_script
and wp_enqueue_style
. The list goes on and on. But did you ever stop to think about what all of these functions have in common? You guessed it! They all work with registries.
Once you see one, you’ll realize that almost everything that extends WordPress uses a registry. Shortcodes, scripts, custom post types, filters, hooks, user roles, custom menus, and even blocks. So how does a registry fundamentally work? How can we create our own registries to make our plugins as extendable as WordPress?
How Most WordPress Registries Work
A registry is nothing more than a way to store data into memory. The registry data is usually an array of objects, but it can be just about anything you want as long as you can access, add to, and remove from it. These can be built on-the fly on each page load, or the registry itself can be stored in a database. You’ll find that most registries in WordPress are built on-the-fly using things like register_post_type
or add_action
.
WordPress usually stores its registry data as a global variable in PHP. This simple approach is mostly because WordPress has been around for a while, and that was the best way to accomplish this at the time. The problem with this approach is that you don’t really want people to be able to directly change the registry. Instead, it’s best to provide everyone with a set of functions that allow them to add, remove, or retrieve items. This keeps people from doing bad practices and messing with the registry in some way in which it is not intended. If you look at one of the oldest APIs in WordPress – the hooks API, you’ll see references to a global $wp_filter
– that’s what actually stores all of the filters and hooks that are added with add_action
and add_filter
!
Instead, it’s best to store a registry inside of a PHP class as a private, static variable. This protects the variable, and allows you to control how the registry can be changed. If you look at one of WordPress’s newest APIs – the blocks API, you’ll see that register_block_type
uses a pattern that does exactly this.
Make Your Own Registry
WordPress has created plenty of registries, and most of the time you don’t need to make your own registry. Every once in a while, however, you’ll find that you need to create your own registry. This usually happens when you’re working with some kind of construct that is not related to WordPress core.
A Dead-Simple Registry
By-far the simplest way to make a registry in WordPress is to simply use WordPress’ apply_filters
to manage it. Something like this would work:
This works really well for dead-simple things, but it has two problems.
- Anyone can manipulate any item in the registry – With this method, you’re passing the entire pre-sanitized registry through a filter, so anyone can manipulate the registry directly however they wish. This could be abused in some way you don’t want.
- The registry is only accessible in this moment – You can only access this registry right now. You could wrap it inside a function or something to make it accessible at any time, but then you’d have to run
apply_filters
every time you make the call. Adding this to a PHP class may be a better option, but that adds enough complexity that you may as well make a full-blown registry.
A Robust Registry
When the apply_filters
method is not going to cut it, I will create a more robust registry somewhat like how the WordPress blocks registry works. In other words, I make a PHP class that manages the registry itself, and another PHP factory class that is used for each item in the registry. This means that the registry is always an array of the same type of PHP class.
I like to create classes that extend PHP’s arrayIterator
class. This allows you to literally use the PHP class like an array, where the actual array itself is the registry. You also get to still use the class as a regular class – with its own methods, and other logic to actually add to the array.
A good example of one of these in-action is Underpin’s registry class, which has a handful of methods to control how work with the registry. Since it is extending ArrayIterator
, you can see here that adding to the registry works a lot like adding to an array.
But there’s a bit more to it than that – you need to actually store the registry somewhere where it can be accessed when you need it. There’s several ways to approach this, but a common strategy is to create a static get_instance
method much like how WordPress’ block registry works.
Now any time you want to access the registry, you can run Custom_Registry::get_instance()
.
Registries in Underpin
Underpin is a WordPress framework that relies heavily on registries to work. everything gets registered against the plugin, and you can access these items whenever you need them. Most registries in Underpin are called loaders, and have a few really handy functions to filter, and find registry items.
A loader registry stores instances a specific type of PHP class. The class depends on what item you’re trying to store. For example, the logger loader stores a registry of different event log types, each one with its own logic to address how logged events for that specific event type works.
The best part? Loaders are actually stored in a registry themselves. Because of this, you don’t have to manually create new methods to access these items, because Underpin is smart enough to check the loader registry to find which item you want.
So, if you made a custom registry, like this:
You can now run underpin()->loader_name()
and it would give you the list of all items in the loader_name
registry. No need to manually create the loader_name
method – Underpin just does that.
From there, you can add to the loader just like any other Underpin using either an array of arguments, or a reference to a class insatnce, like so:
From there, you can retrieve this specific item like so:
Some Ways I’ve Used Custom Registries
In my beer plugin course, I create a registry of beer SRM values. These are unique identifiers that determine the color of the beer, and range from 1-40. With this registry, I’m able to quickly get the beer color from a hex value using beer()->srm()->get( $srm )
. We also use this registry to create a REST endpoint so it can be used in the block editor.
In most of my plugins, I use Underpin’s Options Loader to create a registry of every option that my plugin can manipulate. With this, it is much easier to loop through all options created by the plugin and delete the option when the plugin is uninstalled. This is also really helpful when writing unit tests since you usually want to reset the database each time you run the tests.
Underpin’s BerlinDB database table loader creates a registry of database tables for the same reason – it allows you to wipe out all custom tables by fetching all of the registered tables. This has a lot of the same benefits as the options loader example above.
One of my favorite uses so-far has been in DesignFrame’s instant quote tool. Inside this plugin, I have registered several plugins that we frequently use, as well as some ways in-which using those plugins impact the estimated price. The system does a lot of registry filtering along with some array manipulation to figure out what plugins are actually needed for a site build, and that has a big impact on how the price is calculated.
Conclusion
If you pay attention, you’ll see registries everywhere. Not only in WordPress, but in just about any facet of programming. If you can identify when a registry will help your plugin, and you’re comfortable with making one, your code will become easier to extend, while still giving you a sense of control over how your plugin is extended.
Leave a Reply