Namespacing and Autoloading
Ben on
What Is Namespacing?
Maybe you’ve opened up a file and seen namespace App
up at the top. Maybe you’ve run into a function or class call that Looks\Like\this()
. Or maybe you’ve started using Composers in Sage 10 and you’re wondering how the magic of associating them by class name with partials works (and if you haven’t, we’ll be talking about that later!). All of these things are accomplished through PHP’s support for Namespacing.
Namespacing is a technique and/or language feature that helps to organize related code. The simplest version is to just add something as a prefix to all your functions, classes, etc:
// namespace is "roots"
function roots_gallery() { /* ... */ }
const ROOTS_API = '/* ... */';
class roots_Database { /* ... */ }
This visually and conceptually groups your code together into a “roots” group, and helps prevent naming collisions, but it has a couple of issues:
- You have to write it out, manually, for everything which means you’re more likely to forget to write it, or accidentally write
rooots_
orrootss_
or something - Nothing enforces any of it except you remembering to do it
- If you need more than one “level” of organization, your names start to get long and unwieldy, i.e.
roots_formatting_comments_link()
- Although it helps humans to read your code, the PHP parser will have no knowledge of your namespacing, so it can’t help you out
Fortunately, PHP has a namespacing language feature that can help you out:
// This namespace declaration must *always* be the first line
// of actual code. It establishes the namespace that everything
// else in this file is in, and can't be re-declared.
namespace Roots;
function gallery() { /* ... */ }
const API = 'some scalar value';
class Database { /* ... */ }
// These can now be called elsewhere as:
\Roots\gallery();
\Roots\API;
new \Roots\Database()
namespace
at the top of a file, every function, const
, class, or interface you declare in that file will automatically take on that namespace. This becomes relevant if you need things from another namespace, or need to call things from this namespace elsewhere.Defining deeper organization is also simple:
namespace Roots\Formatting\Comments;
function link() { /* ... */ }
// Can now be called elsewhere as:
\Roots\Formatting\Comments\link();
That looks nice! But how does giving the parser awareness of your namespace structure actually help you?
Being “In” a Namespace
Namespaces are a lot like directories on your computer—in fact they often have a one-to-one mapping with actual directories. When you use PHP’s namespacing, you’ll be able to interact with your namespaced functionality as though each item you define in a namespaced file were an item in a directory.
// Roots/Utilities.php
namespace Roots\Utilities;
class Enqueue {
public function __construct($path, $type) {
/** Do something */
}
}
// Roots/Styles.php
namespace Roots\Styles;
function enqueue() {
// This will fail: You'll get an an error saying something
// like "Uncaught Error: Class 'Roots\Styles\Enqueue' not found"
// because the class 'Enqueue' does not exist in this namespace.
new Enqueue('fonts.css', 'style');
// This will also fail: You'll get an Exception saying something
// like "Uncaught Error: Class 'Roots\Styles\Roots\Utilities\Enqueue'
// not found" because PHP will concatenate namespaces in order to
// resolve them: The namespace defined at the top of this file
// (Roots\Styles) will be combined with the namespace used in the
// class call (Roots\Utilities) to form one invalid
// namespace: Roots\Styles\Roots\Utilities.
new Roots\Utilities\Enqueue('normalize.css', 'style');
// This will succeed: the class is correctly namespaced to
// to Roots\Utilities, where it resides, and has been prefaced
// with a "\" which tells PHP "Don't concatenate this
// namespace: Start looking from the root namespace."
\Roots\Utilities\Enqueue('main.css', 'style');
}
// Roots.php
namespace Roots;
function init() {
// This will succeed, because the full namespace will resolve to
// Roots\Styles\enqueue(), which is the correct location.
Styles\enqueue();
}
As you can see, accessing things in different namespaces is a bit like accessing directories on your computer from the command line. Unfortunately, PHP doesn’t allow you to access namespaces relatively (i.e. you can’t do ..\Styles
), so you have to return to the root every time you want to navigate to another namespace.
Because namespaces written in this way are a language feature of PHP, they can plug into other language features (like autoloading), and can help you when writing code through your IDE’s understanding of functionality and how it’s organized. They are also easily understood by other scripts—like Composer. PHP will also enforce their rules: If you know how to read the error messages PHP throws when you’ve used namespacing incorrectly, they’ll provide more information on how to debug the problem.
“Use”ing Namespaces
It can be a pain to keep typing out full namespaces when you want to use some functionality from a different namespace, and fortunately PHP allows you to “import” things from a namespace with the use
feature.
// Helpers.php
namespace App\Helpers\ThingDoer;
function do_thing() { /** do something */ }
// Assets.php
namespace App\Assets;
use App\Helpers\ThingDoer;
// Now I can call \App\Helpers\do_thing() without
// specifying the entire namespace.
ThingDoer\do_thing();
After use
ing a namespace, you only have to type the last segment of the namespace, instead of the whole thing. There are a few other variations, depending on what you’re doing:
namespace App\Assets;
// This aliases the namespace to `Doer` so we can call
// Doer\do_thing()
use App\Helpers\ThingDoer as Doer;
// This imports our function directly, so we can call
// do_thing()
use function App\Helpers\ThingDoer\do_thing;
// This aliases our function to `doer` so we can call
// doer()
use function App\Helpers\ThingDoer\do_thing as doer;
Gotchas
Although namespacing can be extremely helpful, there are a few things to keep in mind when using it.
Use Fully-Qualified Names to Call Things in Other Contexts
A namespace
declaration at the start of a file is essentially saying “Everything in this file is prefixed by this string, so you don’t need to write it out.”
namespace App\Roots;
hello() { /** do something */ }
/** These are equivalent */
hello();
\App\Roots\hello()
This means that when called outside that file, you need to use the whole name (or at least the “missing part” if you’re in a namespace that is part of its prefix).
Where this is most obvious in a WordPress context is when you’re setting up a hook of some kind.
add_filter
or add_action
are just saying “When you encounter the filter that corresponds to this string, I want you to call the thing that corresponds to this callable.” When that callable is called and executed it is not executed in the context of the file where it is defined. That means that when we put a namespaced function into a hook, we need to use the fully-qualified name:
namespace App\Roots;
function hello () { /** do something */ }
// This will throw an Error along the lines of "Cannot
// find function 'hello'."
add_filter('init', 'hello');
// This will function correctly.
add_filter('init', '\\App\\Roots\\hello');
That can be a lot of typing, though, and I personally really hate escaping all those backslashes. Fortunately PHP provides us with a magic constant called __NAMESPACE__
that is equal to the string value of the current namespace. That means we can do this:
add_filter('init', __NAMESPACE__ . '\\hello');
Of course, if your callable is an anonymous function, then you don’t need to worry about any of that:
add_filter('init', function() { /** do something */ });
Non-Namespaced Things
Technically things that aren’t namespaced are; they just exist in the “root” namespace, which is defined by \
.
// These are the same function call.
get_the_ID();
\get_the_ID();
You can call functions like this from inside a namespace, and they’ll work fine either way (with the root namespace indicator or without).
However, if you define a namespaced function with the same name, it will override the root namespace function within the namespace where it is defined:
// file1.php
function hello() {
echo "I'm not namespaced";
}
// file2.php
namespace App;
function hello() {
echo "I'm in the App namespace";
}
// file3.php
namespace App;
include 'file1.php';
include 'file2.php';
hello();
// "I'm in the App namespace"
Composer and Namespacing
If you aren’t already familiar with Composer, I strongly encourage you to get familiar with it as it is used extensively by Sage and Bedrock and will change your life. Composer can be told how you’re namespacing your functionality and use that to generate an autoloader that makes your functionality available more or less “for free.” (Otherwise you would have to write your own autoloader and/or manually include the correct files for the correct scripts.)
Any project using Composer must have a composer.json
file at its root. Any packages you’ve installed here will already be part of Composer’s autoloading scheme. There is also a section of your composer.json
where you can tell it where to find other things. In Sage, that section looks like this:
"autoload": {
"psr-4": {
"App\\": "app/"
}
}
PSR-4 is not the only standard Composer understands, but it is generally the easiest. I recommend reading the official PSR-4 specification, but the short version is that directory and file names must exactly match their namespace components. When using Composer’s autoloader, you can define a namespace prefix, and the directory mapped to that prefix, and your directory and namespace structure must only match beyond that point. Using the above composer.json
example, the class App\Assets\Cache
would have the following directory and file structure: app/Assets/Cache.php
.
In plain English, the composer.json
is saying:
// Hi, I'd like you to autoload some stuff for me if I ask
// for it.
"autoload": {
// I'll be using the PSR-4 standard for naming
// and file organization.
"psr-4": {
// You can find anything I ask for that has a namespace
// beginning with "App" in the directory "app" (relative
// to the location of this composer.json file).
"App\\": "app/"
}
}
Now Composer knows where to find our files, so we can do this:
// app/Name.php
namespace App;
class Name {
public function get() {
return 'Dwayne "The Rock" Johnson';
}
}
// app/Greetings.php
namespace App;
class Greetings {
public function salutation($name) { return "Good evening, $name."; }
}
// index.php
namespace App;
// This includes Composer's autoload script
require 'vendor/autoload.php';
echo (new Greetings)->salutation((new Name)->get());
// "Good evening, Dwayne "The Rock" Johnson."
Composer’s autoloader must still be loaded manually, but (depending on how your project is structured) you should only need to do that once, by require
ing vendor/autoload.php
.
In Sage, theme dependencies are loaded this way early on in the functions.php
file, making classes available to essentially all other theme code. In Bedrock, site dependencies are loading early in wp-config.php
, making classes available to essentially all other site code. That means if you’re using either of these products you don’t need to worry about loading Composer’s autoloader—it’s already been done for you!
Real-World Example: Sage 10
In the soon-to-be-released Sage 10, we’ve borrowed a concept from Laravel called “Composers” (unrelated to the piece of software called “Composer” that I’ve been talking about for the most of the article). If you used Controller or Sage’s template filters with Sage 9 then you’re already at least a little familiar with what Composers do: they pass data to templates. With Controller and Sage’s filters, you were largely limited to what WordPress considers a template (i.e. index, archive, etc). With Composers you can target any Blade view your theme uses. For a more in-depth discussion of what they do, check out “Working With View Composers in Sage 10” (the name “View Composers” has since been abandoned, but they’re the same thing).
What we’re going to talk about here is the simplified view relationship declaration: The one that works by using the path and filename of a Composer to associate it with a view. In other words, doing this:
// resources/views/partials/content.blade.php
<h1>{{ $title }}</h1>
// app/Composers/Partials/Content.php
namespace App\Composers\Partials;
use Roots\Acorn\View\Composer;
class Content extends Composer {
public function with() {
return ['title' => get_the_title()];
}
}
This works because we’ve defined the App namespace in composer.json
, which allows us to easily discover where this functionality is without being explicity told.
Autoloading
Autoloading is a core PHP functionality: Composer’s autoloading is an implementation of PHP’s native autoloading system. Native Autoloading doesn’t load anything on its own: it provides a hook for you to tell PHP where it can find classes if it fails to find them on its own.
So where did autoloading come from, and what does it do? Well, when you’re writing a short PHP script, you can fit it all in one file:
<?php echo "Good morning!";
Eventually, though, this gets unwieldy, so you start splitting your code into different files:
// file1.php
function salutation($name) { return "Good morning, $name!"; }
//file2.php
function name() { return 'Dwayne "The Rock" Johnson'; }
// file3.php
require 'file1.php';
require 'file2.php';
echo salutation(name());
// "Good morning, Dwayne "The Rock" Johnson!"
Again, fairly easy, but as you write more and more code this doesn’t scale well: You might end up require
ing dozens of files at the top of each script to make sure you have access to everything you need. If only there were some way you could get PHP to automatically provide you with whatever you needed…
// Greetings.php
class Greetings
{
public function salutation($name) { return "Good morning, $name!"; }
}
// Name.php
class Name;
{
public function get() { return "Dwayne "The Rock" Johnson"; }
}
// file.php
echo (new Greetings)->salutation((new Name)->get());
// "Good morning, Dwayne "The Rock" Johnson!"
So most of the power of autoloading is developer convenience: You can organize your functionality and PHP will hand it to you when you need it.
Performance
There is also a small performance benefit to autoloading: Whenever you ask a PHP script to load a file, there is a performance hit as it accesses the file system—the slowest part of the computer (well, except maybe network requests). Autoloading can minimize that performance hit by only requiring you to hit the file system for things you actually need, when you actually need them. You can also ask Composer to generate an optimized autoloader for you that speeds this up even more—just don’t use it in development.
So How Does It Work
There are two parts to making autoloading work:
- You establish a pattern for where scripts can be found and tell PHP about it
- You store and namespace your scripts in the way you said you would
If you’re feeling particularly bullish, you can write an autoloader yourself—just read up on how PHP’s autoloaders are built, and probably familiarize yourself with PSR-4 as well. You don’t need to though, because you’d be reinventing the wheel: Composer has an excellent autoloader that you can use to load anything and everything you might need.
Functions
The autoloading mechanism that PHP uses only supports classes, so strictly speaking there is no way to autoload functions (or other things that can be namespaced). Composer can however be configured to include specific files as part of its “autoload” workflow, essentially require
ing those files and making their contents available to everything downstream:
"autoload": {
"files": [ "src/UsefulLibrary/helper.php" ]
}
These files won’t benefit from any autoloading performance bump, and you should keep in mind that they will all be loaded with every execution—in WordPress, that means they’ll all be loaded on every request—so don’t load too many things in this way.
So What Can All This Do For Me
Namespacing can help you make your code more readable and organized, and adhering to standards like PSR-4 helps make your organization at least somewhat universally parseable. It can give you access to tools like Composer, and often integrates into IDEs or advanced text editors to give you code completion and static analysis. Namespacing is widely used in the PHP community, and understanding how it works will help you to understand how popular libraries work. If you want to distribute your own library to become one of those popular libraries, correctly configured namespacing will make it much easier for other people to use your code. Autoloading can help reduce your workload and your server’s workload. Taken together, these tools can make you and more accomplished, more competent programmer.