Autoloading — spl_autoload_register, PSR-4
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
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();