sajad torkamani

Note: All the code in this article can be found in this GitHub repo.

The trouble with require

Usually, when we want to use a class or function that's defined in another file, we have to require or include them:

<?php

require 'lib/classes/GreeterService.php'; # We must explicitly specify the paths
require 'lib/classes/DateService.php';

$greeter = new GreeterService();
$greeter->greet('Jimmy');

$date = new DateService();
$date->printDate();

But manually requiring files like this becomes tedious and unmaintainable when our project grows. Suppose you decide to move the GreeterService.php file to some other location, you'd then have to update all the require statements.

spl_autoload_register to the rescue

Fortunately, PHP gives us the spl_autoload_register function to make life easier. spl_autoload_register has the following signature:

spl_autoload_register(
	callable $autoload_function = ?, 
	bool $throw = true, 
	bool $prepend = false
): bool

For now, let's focus on the first argument - the autoload_function. Using spl_autoload_register , we can replace the previous code with the require statements to something like this:

<?php

spl_autoload_register('classLoader');

function classLoader(string $className)
{
    $filePath = __DIR__ . "/lib/classes/$className.php";

    if (is_readable($filePath)) {
        require $filePath;
    }
}

$greeter = new GreeterService();
$greeter->greet('Jimmy');

$date = new DateService();
$date->printDate();

How spl_autoload_register works under the hood

Every time you reference a class (e.g., new Foo()), PHP looks at a queue of autoloaders to see if any of them can resolve the given class. spl_autoload_register allows you to add your own autoloader to this queue. This means you can tell PHP how to automatically find classes in certain directories.

Tidying up the custom autoloader

To clean up our code, it's a good idea to move the autoloading logic into a dedicated autoload.php file:

<?php

class Autoloader
{
    public static function ClassLoader(string $className)
    {
        $filePath = __DIR__ . "/lib/classes/$className.php";

        if (is_readable($filePath)) {
            require $filePath;
        }
    }
}

spl_autoload_register('Autoloader::ClassLoader');
# You can define multiple autoloaders:
# spl_autoload_register('Autoloader::ServiceLoader');
# spl_autoload_register('Autoloader::ControllerLoader');
# etc...

Then we can require this autoload file just once at the start of our application:

<?php

require_once __DIR__ . '/autoload.php';

$greeter = new GreeterService();
$greeter->greet('Jimmy');

$date = new DateService();
$date->printDate();

That looks so much cleaner. Now if we want to add new classes under our existing lib/classes directory, our autoload.php file automatically registers these classes. We don't have to require them when we use them.

Wrapping up

Most PHP developers nowadays use Composer and PSR-4 autoloading so that they don't have to bother defining their own autoloaders as we've done in this article. Composer takes care of generating an autoload.php file for you so that you typically just need to do add a require vendor/autoload.php line at the start of your application (see example).

But it's good to understand how things work under the hood and hopefully, this article has helped you understand PHP's autoloading mechanism a little better.

Tagged: PHP