Beyond Prefixing: A WordPress Developer’s Guide to PHP Namespaces

Prefix everything.

It is an adage that is old as the WordPress software itself. Prefixing has been a standard for WordPress developers for so long that it’s hard to imagine doing anything different. But, the time has come for something new. Well, it is long past due, but WordPress lags a bit behind in standard practices in the larger PHP world.

Prefixing is the practice of creating a code-friendly version of your project name and sticking it to the front of functions, classes, and other things in the global namespace. For example, you would name a function tavern_get_post() instead of get_post() to avoid function name clashes, which would result in a fatal error.

Prefixing is one form of “namespacing,” which is just a fancy way of saying that names in this space belong to a specific project. However, prefixing (and suffixing, which is less common) is a hack from a time when no solution existed for the PHP language.

PHP 5.3 introduced an official method of namespacing, so the standard has existed for years. Because WordPress 5.2 bumped the minimum PHP requirement to 5.6, it is time for developers to shed their old habits and catch up to the rest of the PHP world.

Namespace (Almost) Everything

PHP namespacing only covers the following items.

  • Classes
  • Interfaces
  • Traits
  • Functions
  • Constants declared with the const keyword but not define()

When it comes to script handles, image size names, database options, and other items in the global namespace, you must still prefix them. Those are IDs and outside the scope of PHP namespacing.

How to Create Namespaces

Namespaces are simple to declare. At the top of any PHP file that you want to use a particular namespace, declare it as shown in the following code snippet.

<?php

namespace Tavern;

What this line of code does is declare that everything within this particular file has the namespace of Tavern.

Take a look at a simple function under that namespace for outputting a Hello, World! message.

<?php

namespace Tavern;

function hello() {
    _e( 'Hello, World!', 'example-textdomain' );
}

If following the old rules of prefixing, hello() would have been named tavern_hello(). However, that’s not the case with namespaces. The hello() function is encapsulated within the Tavern namespace and will not conflict with other functions named hello().

Classes and interfaces work the same as functions. With a class name of Article, the class file might look like the following.

<?php

namespace Tavern;

class Article {
    // ...
}

Note: There should only ever be one class or interface per file. This is particularly important if you ever plan to use an autoloader.

How to Name Namespaces

Developers like to argue over how to name things, and there is no one-size-fits-all solution. The most important rule is to be unique to avoid clashes with code from other projects. One of the best ways to do that is to use a top-level Vendor namespace with a Package sub-namespace.

Suppose the vendor namespace was Tavern and the project in question was a WordPress theme named News. The namespace for the project might look like the following.

<?php

namespace Tavern\News;

That may be a bit verbose for some developers. If your project’s name is already fairly unique, such as “Awesomesauce,” you may simply want to use the following.

<?php

namespace Awesomesauce;

You will want to come up with some sort of standard convention, at the very least, for yourself. Eventually, you’ll want to get into things like auto-loading, so having a system you follow in all your projects will help. Feel free to peruse the PHP-FIG Autoloader standard.

Importing Classes and Functions into a Different Namespace

When you need to use a class or function from a different namespace than the current namespace, you need to import it. This is done via the use keyword in PHP.

The use statement must come after the namespace declaration. It should also reference the fully-qualified class name. The following code imports the Tavern\Helpers\Post class into a file with a different namespace.

<?php

namespace Tavern\Template;

use Tavern\Helpers\Post;

Once it is imported, you are safe to use the Post class directly as shown in the next snippet.

$post = new Post();

As of PHP 5.6, you can also import functions and constants from other namespaces using the use function and use const keywords, respectively. The following code block demonstrates how to import both a function and a constant.

<?php

namespace Tavern\Template;

use function Tavern\Helpers\func_name;
use const    Tavern\Helpers\CONSTANT_NAME;

Aliasing Classes and Functions

Eventually, you will run into a situation where you need to import a class or function that has the same name as a class or function within the current namespace. You might be thinking that this is the problem that namespaces were meant to solve. Fortunately, PHP provides a method of creating an alias on import.

Suppose you have a class named Tavern\User and need to implement the Tavern\Contracts\User interface. When importing the interface, you will need to create an alias as shown below.

<?php

namespace Tavern;

use Tavern\Contracts\User as UserContract;

class User implements UserContract {
    // ...
}

The as UserContract appended to the end of the use statement creates an alias for the User interface. You can safely use the new UserContract name without error.

Classes, interfaces, functions, and constants all follow the same method for creating an alias.

Organizing Folder Structure Based on Namespaces

It is standard practice in the wider PHP world for namespaces and the project’s file and folder structure to match. Doing this makes it easy for other developers to easily locate code within your project. It also makes it simple to build autoloaders for loading classes on demand.

Generally, all PHP code should go into a /src, /inc, or similarly-named folder in your project. An example plugin file and folder structure might look like the following.

/plugin-name
    /src
        /Core
            /Activate.php
            /Setup.php
        /View
            /Post.php
            /Page.php

If following the same structure with namespaces, the above .php files would contain the following classes.

  • Tavern\Core\Activate
  • Tavern\Core\Setup
  • Tavern\View\Post
  • Tavern\View\Page

Take note that file and folder names are case-sensitive and should match the namespace and class name exactly.

Of course, you are free to follow any convention that you wish. However, the preceding recommendation is good practice and will simplify how you organize your projects in the long term.

Benefits of Using Namespaces

The most obvious benefit is to avoid clashes between classes and functions with the same name. You should use real namespaces for the same reason you used prefixes.

Namespaces help to avoid long class names. Typing long names throughout a large project is a tedious practice at best.

More easily switch implementations by importing. Once you get the hang of importing classes and interfaces from other namespaces, you can switch an implementation of an interface with a single line of code.

Autoloading classes is far easier if you follow the PSR-4: Autoloader standard, which requires at least a top-level namespace.

For developers in the professional space, you will gain a marketable skill beyond the WordPress ecosystem. You will be hard-pressed to find PHP development work if you don’t know how to use namespaces. It is not a tough concept to grasp, but there can be a learning curve for some in practice.

10

10 responses to “Beyond Prefixing: A WordPress Developer’s Guide to PHP Namespaces”

  1. Hey Justin,

    It would be really good if the WordPress core team where to officially document that the \WordPress and \WP namespaces were reserved. This so developers new to namespaces would not think the best way to write code for WordPress is use one of those two _(which they might do if they are thinking their code is “for” use with WordPress….)_

    Don’t you think?

  2. I’ll be honest and say that this article was actually helpful for me. I didn’t bother using PHP Namespaces and kind of forgot how to use them. I’m still doing things the old 5.2 way (because I hated getting 1-star reviews from “unexpected T_FUNCTION”s errors), even if WordPress has officially dropped lower PHP versions.

  3. Very nice, Justin. It’s been interesting how little discussion there has been about using PHP namespaces in WordPress dev. In fact, your blog post last year is one of the few I’ve seen:

    http://justintadlock.com/archives/2018/12/14/php-namespaces-for-wordpress-developers

    Another detailed post by Steve Grunwell:

    https://stevegrunwell.com/blog/php-namespaces-wordpress/

    I think it would be very interesting for you to write about object oriented programming and plugin/theme structure too, based on your vast experience in this space.

    It seems a lot more agencies are starting to maintain their own starter themes and starter plugins (us included) and esp. as developers try to compare WP development with e.g. Laravel, Composer, etc it might be a nice follow up to this exploration of PHP namespaces…

    • I’ve been playing around with fully OOP WordPress development for a few years. It’s a little weird, but it can be done. I’d love to compare/contrast my work against others’. +1 to this idea from me, for what it’s worth.

  4. Great article, Justin.

    Just wanted to add a note regarding constants and namespaces. You actually can use the define function to create a namespaced constant; you just have to indicate the namespace (thanks, magic constants!) For example, the following two lines create an equivalent, namespaced constant.

    [code lang=text]
    const MY_CONSTANT = 'some value';
    [/code]

    or

    [code lang=text]
    define(__NAMESPACE__.'\MY_CONSTANT', 'some value');
    [/code]

    …notably, the const declaration cannot use functions on the right side of the operand… you can still concatenate a string, but, no functions. Conversely, using the define() function, does allow you to use functions on the right side of the operand.

    Cheers!

  5. Aside: it looks like the double-underscores on either side of the keyword NAMESPACE in my previous comment were translated as markdown…in case anyone tries and it seems to not work. :)

Newsletter

Subscribe Via Email

Enter your email address to subscribe to this blog and receive notifications of new posts by email.

%d bloggers like this: