How PHP autoloading works
The PHP autoloader automatically includes a class file the first time that class is referenced. Without an autoloader you would have to write require by hand for every file. Composer generates the autoloader from the rules in composer.json.
Autoloading types in Composer
| Type | Description | Performance |
|---|---|---|
PSR-4 | Namespaces → directories | Good |
PSR-0 | Deprecated standard | Worse than PSR-4 |
classmap | Pre-built map of classes | Best (production) |
files | Files that are always included | — |
Optimizing the autoloader for production
# Basic optimization — generates a classmap for all PSR-4 classes
composer dump-autoload --optimize
# With dev dependencies disabled
composer dump-autoload --optimize --no-dev
# APCu cache (the fastest)
composer dump-autoload --apcu --optimize --no-devAPCu autoloader (maximum speed)
APCu caches class lookup results in memory. It requires the php-apcu extension:
sudo apt install php8.2-apcu
# In composer.json:
{
"config": {
"apcu-autoloader": true
}
}Autoload configuration in composer.json
{
"autoload": {
"psr-4": {
"App\\": "src/",
"Tests\\": "tests/"
},
"classmap": [
"database/seeds",
"database/factories"
],
"files": [
"src/helpers.php"
]
}
}Laravel: full optimization for production
# Optimize all caches
php artisan optimize
# What optimize does:
# php artisan config:cache
# php artisan route:cache
# php artisan view:cache
# composer dump-autoload --optimizeVerifying autoloading
# View the generated classmap
cat vendor/composer/autoload_classmap.php | head -20
# Size of the autoloader files
du -sh vendor/composer/Common Autoloading Errors
Class not found after a deploy
PHP Fatal error: Uncaught Error: Class "App\\Services\\PaymentService" not found in /var/www/html/public/index.php:12The file vendor/composer/autoload_classmap.php is a static snapshot taken at the moment dump-autoload ran. If a new class was added after that and the autoloader was not regenerated, PHP simply does not know about it. The same happens if CI built the classmap on one branch and a different branch got deployed.
Failed opening required vendor/autoload.php
PHP Fatal error: Uncaught Error: Failed opening required 'vendor/autoload.php' (include_path='.:/usr/share/php') in /var/www/html/public/index.php:3The vendor/ folder is usually excluded from git — if composer install was not run on the server after deployment, the autoloader simply does not exist there.
When Classmap Optimization Does Not Fit
- Active development with frequent new classes: after every new class the map has to be regenerated — forget it, and you get "Class not found" exactly like the example above.
- An APCu cache lives in a specific worker's memory: after deploying new code it must be flushed separately, or the worker keeps looking up stale file paths.
- Libraries that create classes dynamically at runtime (
eval, a runtimeclass_alias) are not always captured correctly in the classmap — they fall back to ordinary PSR-4 lookup.
Verifying the Result
# Verbose output shows the number of classes in the map
composer dump-autoload --optimize -v
# Generated optimized autoload files containing 4821 classes
# Confirm a specific class is visible to the autoloader
php -r "var_dump(class_exists('App\\Services\\PaymentService'));"If, after installing Composer and optimizing, the class is still not found, check that the namespace in the code matches the psr-4 key in composer.json letter for letter, including case: on Linux the filesystem is case-sensitive, unlike Windows, where the mistake might not show up for the developer but will surface in production.
| Generation mode | Needs a rebuild after a new class | Where it fits |
|---|---|---|
| No flags | No, resolves PSR-4 rules on the fly | Local development |
--optimize | Yes, the classmap is static | Staging, CI |
--optimize --apcu | Yes, plus flushing APCu on deploy | Production with long-running workers |
The point of this table: the more aggressive the autoloader optimization, the stricter the requirement to regenerate it after every change to the class set — that is the price of speed.
In practice, pin the deploy command in a build script once and for all instead of typing it by hand every time — the human factor is reliably removed by automation, not by a developer's memory.
Also check the optimize-autoloader option right inside the config section of composer.json — then the --optimize flag is applied automatically on every composer install, and there is no need to remember it separately for each environment.