The Composer autoloader is the first piece of PHP code that runs on every request in any modern PHP application. It resolves class names to file paths, and in a large application it does this hundreds or thousands of times per request. By default, it does this work by checking the filesystem, which is expensive. Optimising the autoloader is one of the highest-leverage performance changes you can make, and it requires no application code changes.
This guide covers the three levels of autoloader optimisation, the danger zones that break production deployments, and a safe workflow for enabling classmap authoritative mode.
What the Autoloader Does on Each Request
When your code references a class for the first time, PHP calls the registered autoloader to find the file that defines it. Composer’s autoloader works through a resolution chain:
- Check the classmap. A static array mapping fully qualified class names to file paths. If the class is in the map, return the file path immediately. This is a hash table lookup, which is extremely fast.
- Check PSR-4 prefixes. Convert the namespace to a directory path and check if the file exists on disk. This involves one or more
file_existscalls, each of which is a filesystem stat operation. - Check PSR-0 prefixes. The older autoloading standard. Same filesystem lookup, different path conversion rules.
- Check include paths (if configured). Rarely used in modern applications, but some legacy codebases have fallback include paths.
The classmap lookup is a PHP array lookup: effectively O(1). The PSR-4 fallback involves filesystem calls: slow on spinning disks, still measurable on SSDs, and amplified under load when the filesystem cache is under pressure.
A large Laminas MVC application might autoload 400 to 800 classes per request. If half of those miss the classmap and fall through to PSR-4 resolution, that is 200 to 400 filesystem stat calls per request just for class loading. On a server handling hundreds of requests per second, that filesystem overhead adds up.
Level 1: The Default Autoloader
When you run composer install or composer update, Composer generates an autoloader that includes:
- A classmap built from the
classmapentries in yourcomposer.jsonand your dependencies’composer.jsonfiles - PSR-4 and PSR-0 namespace prefix mappings
Most classes from your application’s src/ directory are loaded via PSR-4 prefix matching, not from the classmap. Every first load of a class within a request requires a filesystem lookup.
This is the slowest mode. It is also the safest, because it can find any class that follows the PSR-4 naming convention, even if the class was created after the autoloader was generated.
Level 2: Optimised Classmap (--optimize-autoloader)
Running composer dump-autoload --optimize or composer install --optimize-autoloader scans every directory registered in PSR-4 and PSR-0 autoload sections and builds a complete classmap of every class it finds.
1 | composer dump-autoload --optimize |
After this, the autoloader checks the classmap first for every class. If the class is in the map, no filesystem lookup is needed. If the class is not in the map, the autoloader still falls back to PSR-4 resolution.
This is significantly faster for production because the vast majority of classes are found in the classmap. The fallback to PSR-4 only triggers for classes that were not present when the classmap was generated, which in a normal production deployment means nothing falls through.
You can also set this in composer.json so it applies automatically:
1 | { |
The risk here is minimal. The worst case is a class that is not in the classmap falls back to PSR-4 resolution, which is exactly what happens without the optimisation. There is no breakage scenario.
Level 3: Classmap Authoritative (--classmap-authoritative)
This is where the performance gain is largest and the danger is highest.
1 | composer dump-autoload --classmap-authoritative |
Or in composer.json:
1 | { |
In authoritative mode, the autoloader only checks the classmap. If a class is not in the map, the autoloader does not fall back to PSR-4. It immediately returns false, which means PHP throws a fatal error: class not found.
The performance benefit is that every class resolution is a single array lookup. No filesystem calls at all. For an application that loads 500 classes per request, you eliminate 500 potential filesystem stat operations.
The risk is that any class not in the classmap at build time becomes invisible to the application at runtime.
What Breaks Under Authoritative Mode
This is where deployments fail. The following patterns are incompatible with classmap authoritative mode, and they are common in legacy PHP applications.
Plugin Systems That Load Classes by Name
If your application resolves class names from configuration or database records at runtime, those classes must be in the classmap at build time:
1 | // This pattern breaks if $pluginClass was added after the classmap was built |
Laminas MVC’s plugin manager system does this extensively. The service manager resolves class names from configuration arrays, and module systems register classes that the autoloader needs to find. If your service manager configuration references a class that was not scanned during composer dump-autoload, authoritative mode will kill the request.
Factory Patterns With Dynamic Class Names
Any factory that constructs class names from strings is vulnerable:
1 | // Common in legacy Zend Framework code |
If all possible values of $type produce class names that exist in the classmap, this works. If any value produces a class name that was missed, it fails.
Test Suites
PHPUnit generates mock classes at runtime. These classes do not exist on disk and are never in the classmap. Running your test suite with classmap authoritative mode enabled will fail immediately.
This is not a problem in practice because you should not use authoritative mode in development or CI environments. But it is worth knowing so you do not debug a confusing CI failure.
Code Generators
If your application generates PHP classes at runtime and then loads them (some ORM proxy generators, some template compilers), those generated classes are not in the classmap.
Doctrine ORM’s proxy classes are a classic example. If you generate proxies during deployment (which you should), they will be in the classmap. If you rely on Doctrine’s lazy proxy generation at runtime, authoritative mode breaks it.
How to Test Before Deploying
Do not enable authoritative mode in production without testing it under realistic conditions first.
Step 1: Audit Your Autoloading Patterns
Search your codebase for dynamic class instantiation:
1 | grep -Prn 'new \$' src/ app/ module/ |
The first pattern catches direct dynamic instantiation. The second catches code that checks for class existence before loading, which suggests conditional class loading. The third is informational but helps map out class references.
Step 2: Build the Classmap and Check Coverage
Generate the optimised classmap and count entries:
1 | composer dump-autoload --optimize 2>&1 | tail -5 |
Composer reports how many classes were found. Compare this to the number of classes your application actually uses. You can get a rough usage count from:
1 | php -d opcache.enable_cli=1 -r " |
If the classmap contains significantly more classes than your application loads, you have good coverage. If there are classes your application loads that are not in the classmap (generated classes, plugin classes loaded from unusual paths), you need to address those before enabling authoritative mode.
Step 3: Run With Authoritative Mode in Staging
Enable authoritative mode in your staging environment and run your full functional test suite plus manual testing of every major feature path. Pay particular attention to:
- Admin panels and configuration pages (these often load classes dynamically)
- Plugin and module systems
- Form handling (Laminas forms are often loaded by configured class name)
- API endpoints that use content negotiation or accept-type-based handlers
- Anything that uses the Laminas service manager’s abstract factory system
Step 4: Monitor for Class-Not-Found Errors
After deploying authoritative mode, monitor your error logs for Class not found fatals. In a well-tested deployment, you will see none. But have a rollback plan: reverting to optimised (non-authoritative) mode is a single Composer command.
Combining With OPcache Preloading
Classmap authoritative mode and OPcache preloading are complementary optimisations that address different parts of the class loading pipeline.
The autoloader resolves a class name to a file path. OPcache caches the compiled bytecode of that file. Preloading goes further: it compiles the file at server start and loads the class into shared memory, so the autoloader is never called for preloaded classes.
The optimal production stack is:
- Classmap authoritative mode eliminates filesystem lookups for class resolution
- OPcache with
validate_timestamps=0eliminates per-request file stat calls for compiled files - Preloading eliminates autoloader calls entirely for your most-used classes
Each layer removes a category of overhead. Together, they reduce class loading from hundreds of filesystem operations per request to zero.
The PHP 8.5 Performance Reality Check covers how these optimisations compare to engine-level improvements for legacy applications, and the Performance Optimisation for Zend Framework Applications chapter covers the framework-specific tuning that maximises the benefit.
Production Deployment Script
Here is a deployment script fragment that handles autoloader optimisation safely:
1 |
|
The important ordering: generate proxies and any other build-time classes before running composer dump-autoload. If you generate classes after building the classmap, those classes will not be in the map, and authoritative mode will fail to find them.
Measuring the Impact
Before and after enabling classmap authoritative mode, measure:
- Autoloader time. If your framework provides a debug toolbar (Laminas Developer Tools, Symfony Profiler), check the autoloader time specifically. Expect a 30 to 60 percent reduction.
- Total bootstrap time. The time from request entry to the start of your controller or handler logic. Expect a 5 to 15 percent reduction.
- Memory usage. The classmap itself uses memory (a large application’s classmap can be several hundred KB), but you save memory from the PSR-4 resolution data structures. The net effect is usually a slight reduction.
- Requests per second under load. This is where the gain compounds. Under high concurrency, filesystem contention from autoloader stat calls becomes a bottleneck. Eliminating those calls improves throughput disproportionately.
If you want the detailed measurement methodology, the PHP Performance Playbook provides step-by-step profiling for each of these metrics. The Modernising Zend Framework Applications guide covers how autoloader optimisation fits into the broader modernisation workflow for legacy PHP applications.
When to Use Each Level
| Environment | Level | Why |
|---|---|---|
| Local development | Default | You need the autoloader to find new classes as you create them |
| CI / testing | Default or optimised | Never authoritative, because test mocks need dynamic loading |
| Staging | Authoritative | Match production configuration to catch issues |
| Production | Authoritative | Maximum performance with no filesystem lookups |
If your application has extensive dynamic class loading that you cannot eliminate, use optimised mode (level 2) in production instead. The fallback to PSR-4 handles the dynamic classes, and you still get the classmap benefit for the static majority. Authoritative mode is only worth the risk when you have tested it thoroughly and you are confident that every class your application loads is in the classmap at build time.
The autoloader is invisible infrastructure. It runs on every request and you never think about it. That makes it easy to ignore. But for a large PHP application under load, the difference between a filesystem-based autoloader and an optimised classmap is measurable, and it costs nothing but a deployment script change to capture.