php

Why require files when you could autoload classes

andersbjorkland

Anders Björkland

Posted on October 14, 2021

Why require files when you could autoload classes

If you're new to PHP or have been coding a while, most of us are familiar with the require or require_once statements [1]. It's a very handy way to split up our logic into separate files. We can keep files at less than 100 lines of code this way. While mammoths are wolly cool, let's keep it small.

Say that we initially have an Elephant class like this:
./Elephant.php

<?php 

class Elephant 
{

    public function trumpet(): void
    {
        echo 'bahruuuuuuhhhhaaaaa';
    }
}
Enter fullscreen mode Exit fullscreen mode

We can use this class in another file:
./index.php

<?php 

require_once 'Elephant.php';

$elephant = new Elephant();
$elephant->trumpet();

// Output: bahruuuuuuhhhhaaaaa
Enter fullscreen mode Exit fullscreen mode

Similar we would use require_once within each file that needs code from somewhere else. We will need to do this as we are going to put together a jazz band of the zoo. Each animal will make a sound so for this we will built an interface:
./AnimalSoundInterface.php

<?php

interface AnimalSoundInterface
{
    public function getSound(): string;
}
Enter fullscreen mode Exit fullscreen mode

We will now make Elephant implement this interface instead, and make a Giraffe as well.
Elephant.php

<?php 

require_once 'AnimalSoundInterface.php';

class Elephant implements AnimalSoundInterface
{
    public function getSound(): string
    {
        return 'bahruuuuuuhhhhaaaaa';
    }
}
Enter fullscreen mode Exit fullscreen mode

Giraffe.php

<?php 

require_once 'AnimalSoundInterface.php';

class Giraffe implements AnimalSoundInterface
{
    public function getSound(): string
    {
        return 'mmphpmffffmmm';
    }
}
Enter fullscreen mode Exit fullscreen mode

Putting these animals into a band and having them perform is a task for a Conductor:
Conductor.php

<?php 

require_once 'Elephant.php';
require_once 'Giraffe.php';

class Conductor
{
    public function perform(): void
    {
        $zooBand = [];
        $zooBand[] = new Elephant();
        $zooBand[] = new Giraffe();

        foreach ($zooBand as $animal) {
            echo $animal->getSound() . PHP_EOL;
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

And let's make a conductor and have it perform for us:
./index.php

<?php 

require_once 'Conductor.php';

$conductor = new Conductor();
$conductor->perform();

/*
 * Output: 
 * bahruuuuuuhhhhaaaaa
 * mmphpmffffmmm
 */
Enter fullscreen mode Exit fullscreen mode

Putting together a zoo band is still very manageable with require_once.

Autoloading and recommendations

PSR-4 is a PHP Standards Recommendations for autoloading of classes from files [2]. This standard makes it possible to build one autoloading implementation and have it work on any code-base that follows it. PSR-4 dictates that classes are placed within a namespace and potential sub-namespaces. The first namespace may be your 'vendor-name'. In our example, just let's say our vendor-name is App. We are not expecting to distribute our code to be used within any other, so this would suffice. Each file but our index.php in our project needs to be supplemented with this namespace right below the initial <?php:
namespace App;

If we run the index.php file now we will get a fatal error. Uncaught Error: Class "Conductor" not found, despite us having the require_once statement. While we are transitioning to using autoloading, it's a little journey. We will now see the flip-side of declaring a namespace for our classes. In index.php add a use-statement:

<?php 

use App\Conductor;

require_once 'Conductor.php';

$conductor = new Conductor();
$conductor->perform();
Enter fullscreen mode Exit fullscreen mode

In fact, let's add use-statements to the other files that uses the require-statement.

Conductor.php

//...
namespace App;

use App\Elephant;
use App\Giraffe;
//...
Enter fullscreen mode Exit fullscreen mode

Elephant.php, Giraffe.php

//...
namespace App;

use App\AnimalSoundInterface;
//...
Enter fullscreen mode Exit fullscreen mode

We are ready to build an autoloader!

Autoloader

The autoloader is a function that will look at each file in our project and determine if it should be loaded, given the way we have used namespaces and use statements.

The following example is fetched from the php-fig/fig-standards github repo, with minor adjustments [3]:

./autoloader.php

<?php
/**
 * @param string $class The fully-qualified class name.
 * @return void
 */
spl_autoload_register(function ($class) {

    // project-specific namespace prefix
    $prefix = 'App\\';

    // base directory for the namespace prefix
    $base_dir = __DIR__ . '/';

    // does the class use the namespace prefix?
    $len = strlen($prefix);
    if (strncmp($prefix, $class, $len) !== 0) {
        // no, move to the next registered autoloader
        return;
    }

    // get the relative class name
    $relative_class = substr($class, $len);

    // replace the namespace prefix with the base directory, replace namespace
    // separators with directory separators in the relative class name, append
    // with .php
    $file = $base_dir . str_replace('\\', '/', $relative_class) . '.php';

    // HERE'S THE MAGIC RESULT:
    // if the file exists, require it
    if (file_exists($file)) {
        require $file;
    }
});
Enter fullscreen mode Exit fullscreen mode

In essence, the autoloader will be called upon wherever there's a use-statement. It will look at the fully qualified class-name and parse it into a require-statement. This means that the require-statements in our code can be removed. So go ahead and remove them from Conductor.php, Elephant.php, Giraffe.php. Our project still requires ONE require-statement though. In our index.php-file require the autoloader:

<?php 

use App\Conductor;

require_once 'autoloader.php';

$conductor = new Conductor();
$conductor->perform();
Enter fullscreen mode Exit fullscreen mode

For a small-scale project it is up to you if you want to implement an autoloader or not. If you are using Composer, the package manager for PHP, then you will have access to its autoloader if you would prefer it. Personally, I think use App\Conductor looks more neat than require_once 'Conductor.php. But if you are building a project without classes, then PSR-4 and its autoloading would not make sense. Go ahead with require instead!

[1] - https://www.php.net/manual/en/function.require.php
[2] - https://www.php-fig.org/psr/psr-4/
[3] - https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-4-autoloader-examples.md

💖 💪 🙅 🚩
andersbjorkland
Anders Björkland

Posted on October 14, 2021

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related