Configure PSR-4 Autoloading with Composer
Set up PSR-4 autoloading in Composer for PHP projects — namespace mapping, classmap generation, files autoloading, optimization levels, production deployment, and framework integration patterns.
Content
Overview
Composer's autoloader eliminates manual require statements by mapping PHP namespaces to directories. When your code references App\Models\User, the autoloader resolves it to src/Models/User.php automatically. PSR-4 is the PHP community standard — every modern framework (Laravel, Symfony, Slim, Laminas) uses it, and every PHP package on Packagist expects it.
The autoloader has a significant performance impact. In development, it checks the filesystem on every class load. In production, you can pre-compute a classmap that resolves every class to its file path in a single array lookup — eliminating filesystem overhead entirely.
PSR-4 Namespace Mapping
The core concept: a namespace prefix maps to a base directory. Everything after the prefix maps to subdirectories.
With this configuration:
- -
App\Models\Userloads fromsrc/Models/User.php - -
App\Http\Controllers\UserControllerloads fromsrc/Http/Controllers/UserController.php - -
App\Services\Payment\StripeGatewayloads fromsrc/Services/Payment/StripeGateway.php
The trailing \\ in the namespace and / in the path are required. Composer builds the mapping from these prefixes.
Multiple Namespace Roots
Large applications often have distinct namespace roots:
The autoload-dev section is only loaded when dependencies are installed without --no-dev. This keeps test classes and database seeders out of production.
Multiple Directories per Namespace
A single namespace can map to multiple directories (useful for gradual migrations):
Composer checks directories in order. If src/Models/User.php exists, it wins. If not, src-legacy/Models/User.php is tried. This pattern enables incremental migration from a legacy codebase.
Directory Structure
The directory structure must match the namespace hierarchy exactly. A mismatch is the most common cause of "class not found" errors:
Each file must declare the correct namespace and class name. The class name must match the filename (case-sensitive on Linux):
Files Autoloading
For global helper functions that do not belong to a class, use the files autoload:
Files in the files array are loaded on every request, regardless of whether the functions are used. Keep this list short — each file adds startup overhead.
Classmap Autoloading
For directories that do not follow PSR-4 conventions (common in legacy code), use classmap:
Classmap scans the specified directories and files, building a map of every class it finds. This works regardless of namespace or directory structure, but it requires running composer dump-autoload whenever a new class is added.
Generating the Autoloader
You must run composer dump-autoload after:
- -Adding or removing a PSR-4 namespace mapping
- -Adding a new file to the
filesarray - -Adding new directories to
classmap
For PSR-4, you do not need to regenerate when adding a new class — the autoloader resolves it dynamically at runtime. But classmap entries require regeneration.
Optimization Levels
Composer offers three optimization levels for production deployment:
Level 0: Default (Development)
Checks the filesystem on every class load. Slowest, but handles dynamically generated classes and new files without regeneration. Always use this in development.
Level 1: Optimized Classmap
Converts all PSR-4 and PSR-0 rules into a classmap. For known classes, the autoloader returns the file path from an array lookup — no filesystem check. Unknown classes still fall back to PSR-4 filesystem resolution.
Level 2: Authoritative Classmap
Same as Level 1, but if a class is not in the classmap, the autoloader immediately returns "not found" without checking the filesystem. This is the fastest option but breaks any code that generates classes at runtime (e.g., Doctrine proxy classes, some test mocking libraries).
Level 1+: APCu Cache
Uses APCu shared memory cache for class lookups. Falls back to filesystem on cache miss. Combines well with Level 1 for environments where APCu is available:
Decision Matrix
| Scenario | Command | Speed | Handles Runtime Classes |
|---|---|---|---|
| Development | `dump-autoload` | Slow | Yes |
| Production (safe) | `dump-autoload -o` | Fast | Partially |
| Production (max speed) | `dump-autoload -a` | Fastest | No |
| Production (APCu) | `dump-autoload -o --apcu` | Fast + cached | Yes |
Production Deployment
The --no-dev flag excludes autoload-dev namespaces and dev dependencies. The --optimize-autoloader flag generates the classmap. The --classmap-authoritative flag enables Level 2.
In CI/CD Pipelines
The --prefer-dist flag downloads zip archives instead of cloning repositories — faster in CI where git history is not needed.
Framework-Specific Patterns
Laravel
Laravel's default composer.json already configures PSR-4:
After adding a new class, Laravel does not require dump-autoload because PSR-4 resolves dynamically. But after adding files entries or modifying namespaces, run:
Symfony
Symfony uses PSR-4 with the App\ namespace pointing to src/:
Symfony's bin/console cache:clear regenerates the service container, which also handles autoloader issues. For production:
Debugging Autoload Issues
Best Practices
- -Use PSR-4 for all application code. One class per file, filename matches class name.
- -Use
filesautoload only for global helper functions. Wrap each function infunction_exists()to prevent redeclaration errors. - -Keep
autoload-devfor test namespaces, factories, and seeders. They have no place in production. - -Run
composer install --no-dev -o -ain production CI/CD pipelines. The speed difference is measurable at scale. - -Use
--classmap-authoritativeonly if your application does not generate classes at runtime. Doctrine proxy generation, PHP-DI compiled containers, and some mocking frameworks are incompatible. - -Verify the autoloader after deployment:
php -r "require 'vendor/autoload.php';"catches missing files early. - -Use multiple namespace roots for domain-driven design — separate
Domain\,Infrastructure\, andApp\into distinct directories.
Common Pitfalls
- -Namespace does not match directory structure — the most common "class not found" error. Check case sensitivity (Linux filesystems are case-sensitive; macOS/Windows are not).
- -Forgetting to run
composer dump-autoloadafter changingcomposer.jsonautoload configuration. - -Putting test autoloading in
autoloadinstead ofautoload-dev— test classes get loaded in production. - -Using
--classmap-authoritativewith code that generates classes at runtime — causes "class not found" errors in production that do not occur in development. - -Not optimizing autoloader in production — the filesystem overhead on every class load adds up. A typical Laravel request loads 200+ classes.
- -Using classmap autoloading for new application code — it is brittle (requires regeneration for every new class). Use PSR-4 instead.
FAQ
Discussion
Loading comments...