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 notdefine()
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 TavernNews;
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 TavernHelpersPost
class into a file with a different namespace.
<?php namespace TavernTemplate; use TavernHelpersPost;
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 TavernTemplate; use function TavernHelpersfunc_name; use const TavernHelpersCONSTANT_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 TavernUser
and need to implement the TavernContractsUser
interface. When importing the interface, you will need to create an alias as shown below.
<?php namespace Tavern; use TavernContractsUser 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.
TavernCoreActivate
TavernCoreSetup
TavernViewPost
TavernViewPage
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.