Menu

richardtape.com

A WordPress Guy

Namespaces in WordPress development

As more and more developers come into the WordPress space (a truly wonderful thing) — many of whom transition from other ecosystems — I often hear or read about how antiquated those developers find WordPress coding practices. You don’t have autoloading? Or dependency management? Or… you get the idea. “Wait… you can’t even use Namespaces in WordPress development? How adorable.”

One of the key reasons – I believe – WordPress has become so incredibly popular is its continuing commitment to backwards compatibility. The changes they make to the core of WordPress very, very rarely cause issues for those upgrading. In fact, one of the finest pieces of coding in WordPress core is the automatic updater. It covers practically everything and is an absolute triumph. You simply can’t do that if you’re worried about breaking things.

I’ll talk more about why that applies in this case especially in a short while, but let’s ensure we’re all on the same page. Whilst this topic can be a little dry, I’m going to explain by example.

What are namespaces?

If you are completely unfamiliar with PHP’s namespaces, you’re not alone. I know a lot of developers who either don’t understand them or, if they do, in most cases don’t see the point of using them.

Namespaces have been around for a long time. They exist in pretty much all programming languages you can name and many you probably can’t. Part of the reason for their pervasiveness is the underlying principle behind them is simple; provide a way to encapsulate items.

Let’s step away from code for a while. Imagine, for a moment, that you are an ardent list-maker. You’re also very particular about order and how you store your precious things. Every week you go shopping and, naturally, you make a list. You’re a 21st Century person so you create your lists on a laptop. You don’t write them with a pen on paper, you’re not Charles Dickens for goodness sake.

Naturally, as you’re creating a shopping list, you name your file shopping-list.txt and you store that in your ‘documents’ directory within a ‘lists’ sub-directory which itself has a subdirectory of ‘shopping’. I mean, where else would you put them? (documents/lists/shopping-list.txt)

However, therein immediately lies a problem. You can’t have two identically named documents in the same directory. Clearly you don’t want to name your shopping list something crazy like shopping-list-23.txt or append the date or anything abhorrent like that. So, naturally, you create a directory structure whereby you have the year and then month and then day and then your shopping-list.txt file. Sweet, blissful organization at its finest.

Now, let’s assume you created one today, Saturday May 27th, 2015. It’d reside in documents/lists/shopping/2015/05/27/shopping-list.txt

What you’ve done is encapsulate your shopping list file in a structure. That structure allows you to have a shopping-list.txt file for each of your shopping trips (within reason).

(Please ignore the rather obvious superfluous name of the file compared to the directory structure. I’m the pedant around here, not you 😉 )

The path to your file (documents/lists/shopping/2015/03/07) is what differentiates each and every one of your shopping-list.txt files.

Whilst we have solved one problem – namely being able to use a semantically correct file name for your shopping list for each time you write one – we have another. What happens if you – a discerning and caring citizen of the internet – wish to share your wonderful shopping list with a friend? Chances are your friend already has a shopping-list.txt file and let’s be honest with ourselves here, it’s unlikely they’re as organized as you.

You send them your file, but – and it’s a big but – the file itself doesn’t contain the encapsulated structure. It only contains the items on our shopping list. It’s a shambles. A clash of filenames. A nightmare.

What would happen, however, if you had a way of sending your friend the entire directory tree (without, for example, having to send them all of your files)? That would be cool. Hey, your friend may even learn how to properly store these things, too.

This round-about and, admittedly, somewhat absurd story is an example of a namespace. Your shopping-list.txt file is namespaced by the path to it on your laptop.

Back in PHP land (and the real world) we can associate the two problems we just encountered in our shopping list scenario with your latest piece of wonder-code. That small-batch, artisanal, gluten-free and vegan-friendly ninjacode you’ve just developed and altruistically want to share with the world.

Your code is built to query the WordPress database. It’s very good at it, in fact. Unfortunately, until this point you’ve been blissfully unaware of naming conventions and namespaces and you’ve placed your code in a class called WP_Query.

Ah! Oopsie daisy. The second that someone running WordPress tries to use your code, everything is going to explode. That class already exists. Now, some of you may just think (and may already be putting this into practice) “why don’t you just rename the class and prefix it?”. That’s certainly one strategy. You may rename it Ninja_WP_Query. No clashes with WordPress core. Hooray! However, come on, deep down, you’re not happy with that. Your code doesn’t query Ninjas. Semantically, it’s not great and let’s face it, that’s a bit like calling your shopping-list.txt file shopping-list-23.txt.

Now, before we discuss how we can solve this problem with PHP namespaces, something important must be mentioned.

A cautionary, sad but hopefully short-lived note

At the moment, WordPress’s minimum required PHP version is 5.2.4. Namespaces were not introduced until PHP version 5.3. This means that should the site for which you are developing still be torturing you with using a version of PHP prior to 5.3 then, unfortunately at the moment, Namespaces are unavailable to you. On top of that, if you’re developing to fully conform with WordPress’s guidelines then, sadly, Namespaces aren’t for you. I am truly hopeful that this cautionary note will disappear in 2015. Hey, if you’re in control of your own stack, then why not read my post on how to migrate from PHP to HHVM?

Going back a step

Originally, I said you had named your class WP_Query. Honestly, you probably didn’t do that. If anything, you’d call it Query (please don’t do that, either, it’s too generic). But I wanted to give a WordPress-example of how this would blow up.

The thing is, when I suggested you rename to <something>_WP_Query, I asked you to hack it. Realistically, the WP_Query class name itself is a hack. The core devs may actually want to call it Query. But, because they’re responsible and realize other people may have already created a class called Query, they prefixed their class with WP_.

Solving name clashes with Namespaces

Let’s work iteratively. Starting with a really simply class.

class Query {

    // Amazing code here

} /* class Query() */

// Elsewhere, in another file [after include()]
$query = new Query();

OK. Great start. Apart from this will explode if anyone else has a class of Query. So;

class WP_Query {

    // Amazing code here

} /* class WP_Query() */

// Elsewhere, in another file [after include()]
$query = new WP_Query();

A little better, but this will still explode within WordPress.

class Ninja_WP_Query {

    // Amazing code here

} /* class Ninja_WP_Query() */

// Elsewhere, in another file [after include()]
$query = new Ninja_WP_Query();

Less likely to explode. More likely to give me nightmares about semantics. So, the PHP Namespace way;

namespace Ninja;

class Query {

    // Amazing code here

} /* class Query() */

// Elsewhere, in another file [after include()]
// This would break as there's no Query() class
//$query = new Query();

// This will not break
$query = new Ninja\Query();

Let’s take a closer look at that last example.

By using the namespace directive we have, for the duration of the file that we’re in (or until another use of the namespace directive, but don’t do that, it’s messy) declared that the classes and functions in that file will be within the scope of that namespace.

If you were following along with the shopping-list.txt file example earlier, this is sort of like moving the file up a directory. So, if initially the file was directly in the root of your hard drive, you would access it via just shopping-list.txt. However, if you move it into the documents directory, (or namespace documents;) then you would access it via documents/shopping-list.txt. If we just tried to access shopping-list.txt we’d get a file-not-found error.

In the same way, we no longer have a Query() class. We only have a Ninja\Query() class.

One thing to note here is that PHP namespaces don’t care about the file structure. The actual location of your class on the filesystem is irrelevant. (This is where the comparison of a namespace to a file path breaks down a bit).

The global namespace

Whenever you declare a class without it being in a namespace, technically speaking, you’re declaring it in what is called the global namespace. This means that the following class is declared in the global namespace;

class Query {

    // Amazing code here

} /* class Query() */

// Elsewhere, in another file [after include()]
$query = new Query();

So the $query variable is now an instance of your Query class. Let’s also add the following

namespace Ninja_WP;
class Query {

    // Amazing code here

} /* class Query() */

// Elsewhere, in another file [after include()]
$query = new Query();

Well. What happens here? If you’re following along, you’ll see that the $query variable is still an instance of the original Query class. It is not an instance of the class we’ve just defined because that class is namespaced. In order to get an instance of that second class, we’d do the following;

$query = new \Ninja_WP\Query();

You may notice that I’ve prefixed Ninja_WP with a backslash. That is optional, and here’s what it means;

Using globally namespaced items from with a namespace

namespace Ninja_WP;
class Query {

    public function __construct() {
        $thing = new \WP_Query( array('...') );
    }/* __construct() */

} /* class Query() */

In this Ninja_WP\Query class you’ll see that we have a construct() method. This method, which is within the namespace Ninja_WP wants to access the WP_Query class that WordPress has declared. It can’t just call $thing = new WP_Query(); because PHP would look for a class called WP_Query within the WP_Ninja namespace. In order to get ‘back’ to the global namespace within which WordPress declares its classes, we use the backslash at the start of our declaration.

In this regards, Namespaces are relative. When we declare a namespace we are creating a new ‘context’. The backslash at the start of the \WP_Query() call resets the context back to the root – or global – namespace. It’s a bit like being on the command line in one directory and opening a file from another directory – you have to specify the path to the file. Let’s say you’re in /Users/rich/docs/stuff/ and you type ./runme.sh. If runme.sh isn’t in the same directory you’re in, nothing is going to happen. If runme.sh resides in /Users/rich/code/ then you’ll need to type /Users/rich/code/runme.sh.

It’s the same with namespaces. If you’re within the context of a namespace, you have to ensure that, if you wish to access a class outside of that namespace, that you tell PHP how to get to that class. For accessing WordPress classes, you just need to prefix them with a backslash as they’re all in the global namespace.

Namespaces can have as many levels of hierarchy as you need them to. \Ninja\WP\Query\User\Meta() could be a class for example.

If WordPress was just beginning life now, chances are they’d create a WP namespace, then possibly something like \WP\Query() and \WP\User().

Using Use

All of that about providing a way to get to a class when you’re within a namespace may sound a little…fudgy. I mean, come on, if I wish to call another class which is 3 levels deep in a namespace, do I really have to type \thing\doodad\watsit\urgh() every time? Fortunately, the answer is…no. No, you don’t.

You can use …err… use.

The use statement allows us to bring a class from one namespace into our current namespace and then just call it by name. i.e.

namespace Ninja;

use \thing\doodad\watsit\urgh;

class Query {

    public static function unicorns() {
        $thing = new urgh(); // instead of $thing = new \thing\doodad\watsit\urgh();
    }/* __construct() */

} /* class Query() */

Doesn’t look super useful when you only use (*snigger*) it the once, but imaging if you wanted to access that class multiple times. Code bloat.

But what happens if we import a class from another namespace and it would clash with another class in our current namespace. No problems. Also, I have no puns for this, you just use as.

I think of the as keyword as a way to provide a nickname to a class. Use of them is pretty simple;

namespace Ninja;

use \thing\doodad\watsit\urgh as awesome;

class Query {

    public static function unicorns() {
        $thing = new awesome();
    }/* __construct() */

} /* class Query() */

Putting it all together; Using Namespaces in WordPress development

The first thing you may want to do, if you intend to use namespaces, is check that doing so isn’t going to blow up someone’s site. PHP has a built in function for this called version_compare(). You can do something along the lines of;

if ( ! function_exists( 'version_compare' ) || version_compare( PHP_VERSION, '5.3.0', '<' ) ) {
    return;
}

Note: You may want to do some error logging or supply a notice rather than just blindly returning, but you get the idea.

When referencing a class method which is in a namespace in WordPress, you do it in a very similar way you’d access a class method outside of a namespace. i.e. if your current code is

add_action( 'init', array( 'Ninja_Query', 'my_method' ) );

And you decide to namespace your stuff, then you’d simply replace the above with

add_action( 'init', array( '\Ninja\Query', 'my_method' ) );

Does WordPress core use Namespaces?

No. Due to the aforementioned restriction of namespaces being PHP 5.3+ and WordPress working on the 5.2 branch, WordPress core doesn’t use namespaces.

Interestingly (or… not) if you do a search for the string ‘namespace’ in WP core, you’ll get 742 results in 35 files. Mostly in the SimplePie library and some in javascript. But not a single namespace declaration.

My hope is that by the end of 2016 this will change.

Caveats

A namespace can’t just be a number – it must start with a letter

myapi\1 is invalid, so instead try myapi\v1

PHP namespaces are case-insensitive

mycoolnamespace\subnamespace is the same as mycoolnamespace\SubNaMEsPACe

OK, we’re just about done. Now, go forth and use namespaces (responsibly) in your code. It could be said that not using namespaces is irresponsible in 2015. As is usually the case, your mileage will vary – if you know that your code is to be used in an applicable environment then you should namespace your code. If you’re unsure or you absolutely know that you’re developing for PHP < 5.3 then, for now, don't use them. With great power...

Comments

Terrific article on namespaces, Rich! Great examples too. Thank you!

I’d find it interesting to know how many plugins in the wordpress.org directory are currently using namespaces.

My understanding is that we have to refrain from using namespaces in WP themes on .org (or they’d be rejected by the Theme Review Team), but we can use namespaces in plugins as long as we specify required PHP versions. Sound right?

Rich says:

Thanks, Eric. Yep that sounds right to me, I think 5.2 support is a requirement for themes, but plugins have a way to detect the PHP version early enough and run away if necessary. I too would be interested in finding out how many plugins use namespaces. Might be a fun project.

Robert says:

Nice article!

If i’m not wrong wordpress now requires 5.3 as the lowest version right?

If that is correct why don’t they start the transition by inheriting all classes so they have backwards compatibility for some time until developers update their code. For example, having 2 wp queries :

1 wp_query
2 wpquery (empty that inherits old wp query)

But WordPress strongly needs a complete redesign of its architecture. Not sure if we’ll ever see such thing happen, though.

Rich says:

Hi Robert,

Thanks for the comment. The recommended version is 5.6. However, WP core currently needs to support the 5.2 branch and as such namespaces are off the table. There is still a significant number of WordPress websites that run on PHP 5.2 (mainly because of hosts who don’t upgrade). There’s a lot of pressure for the decision to be made to up the minimum required version, however, the numbers speak for themselves. When that number of sites running the old version drops (i.e. when their hosts upgrade), the bump will happen. Here’s the minimum requirements for hosts: https://codex.wordpress.org/Hosting_WordPress for reference.

As for a complete redesign… patches welcome 🙂

Leave a Reply