0

Autoloading — spl_autoload_register, PSR-4

Intermediate5 min read·php-07-019
interviewpsr

Concept

Autoloading eliminates the need for manual require statements for every class file. PHP's autoloading mechanism calls a registered function when a class is referenced but not yet defined, giving the autoloader a chance to load the file containing that class.

spl_autoload_register($callable) registers an autoload function. PHP maintains a stack of autoloaders; when a class is needed, each is called in order until the class is found. The classic implementation maps class names to file paths using some naming convention.

PSR-4 is the autoloading standard all modern PHP code follows. It maps a namespace prefix to a base directory, then translates the remainder of the fully-qualified class name into a relative path: App\Http\Controllers\UserController with prefix App\ mapped to app/ becomes app/Http/Controllers/UserController.php. Composer implements PSR-4 autoloading based on composer.json's autoload.psr-4 section.

How Composer autoloading works: composer dump-autoload generates vendor/autoload.php and vendor/composer/autoload_psr4.php. The generated autoloader is registered via spl_autoload_register. When a class is referenced, the autoloader computes the file path from the namespace and requires it. No more manual require_once for every class.

Class maps: Composer can also generate a class map (composer dump-autoload --optimize): an array of ClassName => filePath for all classes. Class map lookups are a hash table O(1) instead of a filesystem path computation — significantly faster in production. Laravel's composer install --optimize-autoloader enables this.

Code Example

php
<?php
declare(strict_types=1);

// Manual spl_autoload_register (what Composer does internally)
spl_autoload_register(function (string $className): void {
    // Convert namespace separator to directory separator
    $file = __DIR__ . '/' . str_replace('\\', '/', $className) . '.php';
    if (file_exists($file)) {
        require $file;
    }
});

// PSR-4 autoloader implementation
spl_autoload_register(function (string $className): void {
    $namespaceMap = [
        'App\\'  => '/var/www/app/',
        'Tests\\' => '/var/www/tests/',
    ];
    foreach ($namespaceMap as $prefix => $baseDir) {
        if (!str_starts_with($className, $prefix)) continue;
        $relativeClass = substr($className, strlen($prefix));
        $file = $baseDir . str_replace('\\', '/', $relativeClass) . '.php';
        if (file_exists($file)) {
            require $file;
            return;
        }
    }
});

// Usage — no require needed
$user = new \App\Models\User(); // autoloader loads app/Models/User.php

// composer.json autoload section
/*
{
    "autoload": {
        "psr-4": {
            "App\\": "app/",
            "Database\\": "database/"
        },
        "classmap": ["database/migrations"],
        "files": ["app/helpers.php"]
    }
}
*/

// After adding a class, run:
// composer dump-autoload         (for development)
// composer dump-autoload --optimize  (for production — generates classmap)

// Multiple autoloaders in a stack
spl_autoload_register(function($class) { /* first autoloader */ });
spl_autoload_register(function($class) { /* second autoloader */ });
// PHP tries each in registration order; stops when class is found

// Get registered autoloaders
$loaders = spl_autoload_functions();