How to use PHP autoloader to require files containing namespaced classes
If you're building an object-oriented PHP application that's larger than a handful of files, it can be cumbersome to require them one by one. On top of that, you have to manually keep adding your new files. And if you have classes relying on other classes, you have to keep track of the order in which you require them.
There's one function that can solve these challenges and automatically include your classes: spl_autoload_register()
. Here's how to make it work.
Setting up your project
Let's assume you have a project with a file structure like this:
App.php
|- Command
|- Command.php
|- Debug.php
|- Console
|- Argument.php
|- Option.php
And each file uses a namespace to match their path (Command\Debug
), plus one general namespace to encapsulate the app (App
):
App.php
namespace App;
class App {}
Command.php
namespace App\Command;
abstract class Command {}
Debug.php
namespace App\Command;
class Debug extends Command {}
Argument.php
namespace App\Console;
class Argument {}
Option.php
namespace App\Console;
class Option {}
In summary, the App
namespace exists to group your classes together, while Command
and Console
make up the path in which their respective files are found.
Unless you're building a library, your app will have an entry point, and in this case that's App.php
. In other words, App.php
contains the first class that gets instantiated and responsible for working with the remaining classes.
Abstracting the autoloading process
I prefer not to litter App.php
with code that doesn't necessarily pertain to the execution of the app. Because of this, I create a separate file called Autoloader.php
and place it at the same level as App.php
.
App.php
Autoloader.php
|- Command
|- Command.php
|- Debug.php
|- Console
|- Argument.php
|- Option.php
Autoloader.php
<?php
namespace App;
/**
* @author Ryan Sechrest
* @package App
*/
class Autoloader
{
/**
* Register autoloader
*/
public function __construct()
{
spl_autoload_register([$this, 'register']);
}
/**
* Register autoloader to require classes on demand
*
* 1. Remove app name from namespace: 'App\Foo\Bar' => 'Foo\Bar'
* 2. Convert namespace to path: 'Foo\Bar' => 'Foo/Bar'
* 3. Append PHP file extension: 'Foo/Bar' => 'Foo/Bar.php'
* 4. Require file if it exists
*
* @param string $class
*/
public function register(string $class): void
{
$class = substr($class, strlen(__NAMESPACE__ . '\\'));
$class = str_replace('\\', '/', $class);
$class = $class . '.php';
if (!file_exists($class)) {
return;
}
require_once $class;
}
}
The default constructor calls spl_autoload_register()
and passes the Autoloader
instance and register()
method as a callback to spl_autoload_register()
. Also note that $class
is being passed to you by the callback.
Let's take a closer look at register()
.
Once we have the class name, we remove App
from the namespace, because as mentioned earlier, that name only exists to logically group our classes. In other words, none of our classes are nested in a directory called App
.
We then convert the back slashes from our namespace (e.g. Console\Argument
) to forward slashes (e.g. Console/Argument
), and you can see how this is looking more like a path to a file now.
Last, we append .php
to the class, which gives us the path to our file (e.g. Console/Argument.php
).
Initializing the autoloader
The only step left is to actually initialize our Autoloader
class. A good place for that would be the default constructor in our App
class.
App.php
namespace App;
class App
{
public function __construct()
{
require_once 'Autoloader.php';
new Autoloader();
}
}
Now any time a class is utilized in our application, PHP will look for a class based on the rules within our Autoloader
and include it via require_once
as defined in register()
automatically.
Hopefully this provided some clarity on how the autoloader works, and if you have any questions or comments, feel free to leave them below.
Featured image by Markus Winkler.