Using PHP namespaces in WordPress plugins + creating an autoloader

rmorse

Ross Morsali

Posted on August 19, 2021

Using PHP namespaces in WordPress plugins + creating an autoloader

I've been working on upgrading one of my WordPress plugins - Search & Filter for some time now, quite the overhaul you might say...

When planning this update there were a bunch of things I knew I needed to implement. I won't bore you with all the details, but two tasks I wanted to tick off were:

  1. Use PHP namespaces
  2. Setup an autoloader

PHP namespaces

The plugin is getting quite big, and having everything as a class with a unique name was getting a bit messy.

PHP namespaces solve this issue perfectly as you can scope your classes and then you only need to worry about the uniqueness of your top-level namespace (uniqueness is important in WP plugins in order to avoid conflicts with other plugins).

This also lends itself very nicely to consistent project structure, because you will likely nest your files in directories matching your namespaces - everything becomes more predictable and uniform.

PHP autoloader

When PHP applications grow in size, rather than having to require every other .php file in a long list (or by some other method) it's good practice to automatically load them.

As long as your application has a predictable structure (namespace and class names), you don't need to specify which files to load because we can predict where the relevant file is for a particular class (more on that later).

What's also great about autoloaders is they only load files at the last minute - when a class is used - giving your application a lighter memory footprint.

These two concepts absolutely go hand in hand.

WordPress enters chat...

Ok, so we know from the above that if our plugin is going to grow that we're going to want to use both namespaces and an autoloader.

And if we're good developers, we also want to follow the WordPress coding standards.

Coding standards are a good thing and often in the life of a developer, we'll find conflicting scenarios where we might want to use one set of standards or the other.

The problem here is, while WordPress coding standards cover a lot they don't mention anything about namespaces.

2023 Correction: Looks like a section on namespaces has now been created and that they follow the same convention described in this post.

WordPress PHP Coding Standards

In the naming conventions, it's stated that class names should be cased like this:

My_Class
Enter fullscreen mode Exit fullscreen mode

And that they should have a corresponding filename something like this:

class-my-class.php
Enter fullscreen mode Exit fullscreen mode

The class name is lowercased, underscores are hyphenated and the filename is prepended with class-.

So far so good.

Plugin Development Best Practises

There is a section in the best practises which is hugely important:

Prefix everything!

When a site can have hundreds of plugins, the only way our code won't be messed with is if we prefix everything (including class names).

You might want to go with a simple abbreviation prefix, eg, if your plugin was called Speed Up, you could use SU_My_Class but in my opinion (and in the examples) the full name is better, so in theory, the ideal class name would be:

Speed_Up_My_Class
Enter fullscreen mode Exit fullscreen mode

It's already getting cumbersome, but it is necessary.

This would ideally be in a file:

class-speed-up-my-class.php
Enter fullscreen mode Exit fullscreen mode

Switching to namespaces

When structuring a plugin like this it is common to find yourself with tons of files starting with class-speed-up-... .php - to me it's not particularly elegant, but it's necessary because it accurately represents the class name, which has been prefixed too Speed_Up_...

If we switch to namespaces we can alleviate some of this pain and repetition:

namespace Speed_Up;

class My_Class {

}
Enter fullscreen mode Exit fullscreen mode

The great thing here is, now My_Class lives inside the Speed_Up namespace, we don't need to worry about prefixing it with Speed_Up again. By using a namespace, we've kind of prefixed everything inside of it automatically and it won't conflict with other plugins.

This is how we would use the class now:

$my_class = new \Speed_Up\My_Class;
Enter fullscreen mode Exit fullscreen mode

So I think there is not much complexity in this process - just start using them and drop the plugin prefix from all your class names (and filenames) - to be honest I think it's keeping in line with the spirit of prefix everything.

2023 Update: WordPress coding standards now recommends this as the way to use namespaces.

A quick note about namespaces - they support nesting! So you can keep adding names spaces and sub-levels to improve your code organisation - eg:

Speed_Up\Frontend\Scripts\Register
Enter fullscreen mode Exit fullscreen mode

Setting up the autoloader

Now we've got the namespace out of the way let's head back to the autoloader.

There are a few ways to setup autoloaders (each with its own unique benefits) which I'm not going to get into right now - I'm going to go for the easiest and most straightforward method while also keeping things in the spirit of the WordPress coding standards.

Setting one up requires using built-in PHP features.

To implement this in a plugin would require adding some code at the top of your plugins main php file - if we're sticking with the name speed-up then it would be located:

wp-content/plugins/speed-up/speed-up.php
Enter fullscreen mode Exit fullscreen mode
  • We could also create a new file autoload.php in the root directory to store our autoloader - which is something you'll probably see quite often when reading up on the subject. *

If we add the first example from the PHP docs:

spl_autoload_register(function ($class_name) {
    include $class_name . '.php';
});
Enter fullscreen mode Exit fullscreen mode

Then we'll be all set to go! Well, kind of.

What this code does is register a callback to be used, to specify which filename/path we should look in order to find a class.

Roughly how it works:

When a class is first used, eg: $my_class = new My_Class() and it is not yet loaded, the function we passed to spl_autoload_register will be run to try to resolve the filename for the class.

It is passed in the $class_name, and then you can see the file is loaded via include. In the example above, based on the class name the file

My_Class.php would attempt to be loaded.

Also, something worth observing, when a class is used in a namespace, the full namespace + class name is passed through $class_name.

Writing an autoloader for a WordPress plugin

So let's apply the coding standards to the autoloader and calculate the correct file path based on namespace and class name.

What we want to achieve:

  • Class filenames must be prefixed with class-...
  • Class filenames should be lowercase
  • Underscores should be replaced with dashes

And we're going to convert namespaces. Classes belonging to a namespace should live in a folder (namespace name) that follows the conversion process of a class. So we need to add:

  • Namespace names will be converted to lowercase
  • Namespace names will have underscores replaced with dashes
  • Namespaces will be folders so convert \ to a directory path

In addition - because the root plugin folder "is the plugin" and already has a directory name relevant to the plugin name, eg - speed-up we shouldn't create another folder called speed-up just to match our Speed_Up plugin namespace, it's unnecessarily repetitive and we'll actually remove that, and instead, we'll create a folder called includes for our classes instead.

So our plugin structure would look like this:

speed-up // top-level plugin folder
-- speed-up.php // main plugin file
-- readme.txt // required with every plugin
-- includes // this folder is where we store all our classes
---- database // folder for the sub namespace Database
------ class-connect.php // class Connect
       class-worker.php // class Worker
Enter fullscreen mode Exit fullscreen mode

The class Worker in includes\database\class-worker.php above would be accessed like this:

Speed_Up\Database\Worker
Enter fullscreen mode Exit fullscreen mode

For me, this is the perfect balance of adding namespaces to WordPress plugins while keeping in the spirit of the coding standards.

The code

To parse the name space and class names, and follow those above conversion rules (class name -> path + filename) would result in some PHP code like this:

// Define the main autoloader
spl_autoload_register( 'speed_up_autoloader' );
function speed_up_autoloader( $class_name ) {

    // These should be changed for your particular plugin requirements
    $parent_namespace = 'Speed_Up';
    $classes_subfolder = 'includes';

    if ( false !== strpos( $class_name, $parent_namespace ) ) {
        $classes_dir = realpath( plugin_dir_path( __FILE__ ) ) . DIRECTORY_SEPARATOR . $classes_subfolder . DIRECTORY_SEPARATOR;

        // Project namespace
        $project_namespace = $parent_namespace . '\\';
        $length = strlen( $project_namespace );

        // Remove top-level namespace (that is the current dir)
        $class_file = substr( $class_name, $length );
        // Swap underscores for dashes and lowercase
        $class_file = str_replace( '_', '-', strtolower( $class_file ) );

        // Prepend `class-` to the filename (last class part)
        $class_parts = explode( '\\', $class_file );
        $last_index = count( $class_parts ) - 1;
        $class_parts[ $last_index ] = 'class-' . $class_parts[ $last_index ];

        // Join everything back together and add the file extension
        $class_file = implode( DIRECTORY_SEPARATOR, $class_parts ) . '.php';
        $location = $classes_dir . $class_file;

        if ( ! is_file( $location ) ) {
            return;
        }

        require_once $location;
    }
}
Enter fullscreen mode Exit fullscreen mode

I've also setup a tiny demo plugin using the above for you to play with over on GitHub:

Github Repo

Are you doing it a different way? No technique is perfect and there is always room for improvement - let me know your thoughts!

If you want to read more articles like this you can follow me on Twitter to keep up to date.

πŸ’– πŸ’ͺ πŸ™… 🚩
rmorse
Ross Morsali

Posted on August 19, 2021

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related