0

Autoloading — the mechanism that makes require() unnecessary

Beginner5 min read·eng-12-014
psr

Concept

Autoloading — the mechanism that automatically loads a PHP class file the first time the class name is used, without explicit require or include statements.

Without autoloading: Every PHP file that uses User must require 'User.php'. For large projects with hundreds of classes, this is unmaintainable.

PHP's autoload mechanism:

  1. When PHP encounters an undefined class, it calls all registered autoload functions.
  2. Each autoload function receives the fully-qualified class name as a string.
  3. The function maps the class name to a file path and requires it.
  4. If the file is loaded and the class is now defined, execution continues.

spl_autoload_register(callable $fn): Registers an autoload function. Multiple autoloaders can coexist.

PSR-4: The standard for PHP autoloading (PSR = PHP Standards Recommendation). Maps a namespace prefix to a base directory. App\Models\Usersrc/Models/User.php. Composer implements PSR-4.

Composer's autoloader:

  • composer.json: "autoload": {"psr-4": {"App\\": "app/"}}.
  • composer dump-autoload generates vendor/autoload.php.
  • require 'vendor/autoload.php' loads the autoloader.
  • Now any class in app/ matching the App\ namespace is auto-required.

Class maps: An alternative to PSR-4 where Composer scans directories and builds a class → file map at dump time. Faster at runtime (hash map lookup vs string manipulation), but requires dump-autoload when files change.

OPcache + autoloading: With OPcache, the require on first use caches the compiled file. Subsequent uses skip the file load entirely. opcache.preload can pre-require all classes before any request.

Code Example

php
<?php
// Without autoloading — explicit requires
require 'app/Models/User.php';
require 'app/Services/OrderService.php';
require 'app/Repositories/OrderRepository.php';
// 300 classes = 300 require statements — unmaintainable!

// Manual PSR-4 autoloader (what Composer does for you)
spl_autoload_register(function (string $className): void {
    // 'App\Models\User' → 'app/Models/User.php'
    $prefix    = 'App\\';
    $baseDir   = __DIR__ . '/app/';

    if (!str_starts_with($className, $prefix)) return; // not our namespace

    $relativeClass = substr($className, strlen($prefix));                     // 'Models\User'
    $file          = $baseDir . str_replace('\\', '/', $relativeClass) . '.php'; // 'app/Models/User.php'

    if (file_exists($file)) require $file;
});

// Now this works without any explicit require:
$user = new \App\Models\User(); // autoloader loads app/Models/User.php on demand

// composer.json
/*
{
    "autoload": {
        "psr-4": {
            "App\\": "app/",
            "Tests\\": "tests/"
        },
        "classmap": ["database/seeders", "database/factories"],
        "files": ["app/helpers.php"]  // loaded on every request (for global functions)
    }
}
*/

// After adding a new class or changing namespaces:
// composer dump-autoload
// This regenerates vendor/composer/autoload_psr4.php

// In public/index.php (and all entry points):
require __DIR__ . '/../vendor/autoload.php'; // one line — all classes available

// Checking if a class is autoloadable without loading it
$autoloader = require 'vendor/autoload.php';
$classmap   = $autoloader->getClassMap();
echo isset($classmap[\App\Models\User::class]) ? 'found in classmap' : 'PSR-4 lookup';

// Laravel's optimize:
// php artisan optimize → generates bootstrap/cache/compiled.php (classmap-like)
// Faster than PSR-4 string manipulation at runtime