Using PHP namespaces in WordPress plugins + creating an autoloader
Tags: wordpress, php, webdev
Published: 2021-08-19 10:28:44

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](https://developer.wordpress.org/coding-standards/wordpress-coding-standards/php/).

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](https://developer.wordpress.org/coding-standards/wordpress-coding-standards/php/) cover a lot they don't mention anything about namespaces.~~

**2023 Correction:** Looks like [a section on namespaces](https://developer.wordpress.org/coding-standards/wordpress-coding-standards/php/#declare-statements-namespace-and-import-statements) has now been created and that they follow the same convention described in this post.

##WordPress PHP Coding Standards

In the [naming conventions](https://developer.wordpress.org/coding-standards/wordpress-coding-standards/php/#naming-conventions), it's stated that class names should be cased like this:

```
My_Class
```

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

```
class-my-class.php
```

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](https://developer.wordpress.org/plugins/plugin-basics/best-practices/) 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
```

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

This would ideally be in a file:
```
class-speed-up-my-class.php
```

## 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:

```php
namespace Speed_Up;

class My_Class {

}
```
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:
```php
$my_class = new \Speed_Up\My_Class;
```
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](https://developer.wordpress.org/coding-standards/wordpress-coding-standards/php/#declare-statements-namespace-and-import-statements).

**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
```

## 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](https://www.php.net/manual/en/language.oop5.autoload.php). 

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
```
* 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:
```php
spl_autoload_register(function ($class_name) {
    include $class_name . '.php';
});
```
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
```
The class `Worker` in `includes\database\class-worker.php` above would be accessed like this: 
```
Speed_Up\Database\Worker
```
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:

```php
// 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;
    }
}
```
I've also setup a tiny demo plugin using the above for you to play with over on GitHub:

[Github Repo](https://github.com/rmorse/speed-up)

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](https://twitter.com/rmors_) to keep up to date.