Why require files when you could autoload classes
Tags: php
Published: 2021-10-14 14:35:18

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]](#ref1). 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
<?php 

class Elephant 
{
    
    public function trumpet(): void
    {
        echo 'bahruuuuuuhhhhaaaaa';
    }
}
```

We can use this class in another file:
`./index.php` 
```php
<?php 
    
require_once 'Elephant.php';

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

// Output: bahruuuuuuhhhhaaaaa
```

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
<?php

interface AnimalSoundInterface
{
    public function getSound(): string;
}
``` 

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

require_once 'AnimalSoundInterface.php';

class Elephant implements AnimalSoundInterface
{
    public function getSound(): string
    {
        return 'bahruuuuuuhhhhaaaaa';
    }
}
```

`Giraffe.php`
```php
<?php 

require_once 'AnimalSoundInterface.php';

class Giraffe implements AnimalSoundInterface
{
    public function getSound(): string
    {
        return 'mmphpmffffmmm';
    }
}
```

Putting these animals into a band and having them perform is a task for a Conductor:
`Conductor.php`
```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;
        }
    }
}
```

And let's make a conductor and have it perform for us:
`./index.php`
```php
<?php 
    
require_once 'Conductor.php';

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

/*
 * Output: 
 * bahruuuuuuhhhhaaaaa
 * mmphpmffffmmm
 */
```

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]](#ref2). 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
<?php 

use App\Conductor;
    
require_once 'Conductor.php';

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

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

`Conductor.php`
```php
//...
namespace App;

use App\Elephant;
use App\Giraffe;
//...
``` 

`Elephant.php, Giraffe.php`
```php
//...
namespace App;

use App\AnimalSoundInterface;
//...
``` 

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]](#ref3):

`./autoloader.php`
```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;
    }
});
```

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
<?php 

use App\Conductor;
    
require_once 'autoloader.php';

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

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!

<span id="ref1">[1]</a> - https://www.php.net/manual/en/function.require.php
<span id="ref2">[2]</a> - https://www.php-fig.org/psr/psr-4/
<span id="ref3">[3]</a> - https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-4-autoloader-examples.md