You do not need a require() line for every class. PHP calls registered autoloader functions when it first meets a class name it has not loaded. Composer generates that autoloader from the mappings in your composer.json. You include vendor/autoload.php once in the entry point, declare a PSR-4 mapping, and then new AcmeControllerHomeController() loads src/Controller/HomeController.php automatically, provided the namespace, directory, filename, and capitalization line up.
How the pieces fit together
Three things cooperate here, and it helps to keep their jobs separate:
As an Amazon Associate I earn from qualifying purchases.
- Your mapping lives in
composer.json. It says which namespace prefix belongs to which directory. - Composer reads that mapping and writes
vendor/autoload.phpplus supporting files invendor/. You do not edit these generated files. - PSR-4 is the naming convention that turns a fully qualified class name into a file path. The autoloader applies that convention at runtime, one class at a time.
Composer’s basic usage guide describes this generated-autoloader workflow, and the PSR-4 specification defines the path rules.
Declare the namespace-to-directory mapping
Add an autoload block to composer.json. For a framework whose application classes live under the Acme namespace, the mapping looks like this:
#1 Best Overall
{
"autoload": {
"psr-4": {
"Acme\": "src/"
}
}
}
In JSON, each backslash in a namespace is written as \, so the key above represents the prefix Acme. The value src/ is the base directory for that prefix. The trailing namespace separator matters: Composer’s composer.json schema documentation notes that it prevents prefix collisions, so a prefix of Foo would otherwise also match classes in FooBar.
Build a minimal project and wire the entry point
A small layout that works with the mapping above:
project/
composer.json
public/index.php
src/
Controller/HomeController.php
Follow these steps in order.
- Add the
autoloadblock shown above tocomposer.json. - From the project root, run
composer dump-autoload. If you use a localcomposer.phar, runphp composer.phar dump-autoloadinstead. This regenerates Composer’s autoloader and createsvendor/autoload.phpif it does not exist yet. - Create
src/Controller/HomeController.phpwith a namespace that matches its path:<?php namespace AcmeController; class HomeController { public function index(): string { return 'Hello from the home page'; } } - Include the generated autoloader once in the entry point. In
public/index.php:<?php require dirname(__DIR__) . '/vendor/autoload.php'; $controller = new AcmeControllerHomeController(); echo $controller->index();
The path in require depends on where your entry point sits. Here __DIR__ is public/, so dirname(__DIR__) is the project root. Composer’s own example follows the same pattern: include vendor/autoload.php, then instantiate a namespaced class.
Notice that HomeController.php is never required by name. PHP reaches new AcmeControllerHomeController(), the registered autoloader strips the Acme prefix, maps ControllerHomeController to src/Controller/HomeController.php, and loads that file.
Quick wins for a faster PC:
Clear out junk files and repair common Windows errorsFree Scan →Scan for outdated or missing drivers - takes under a minuteDriver Scan →Repair Windows errors before they cause bigger problemsFix Now →Rank #2
Name namespaces, folders, and files so the mapping resolves
PSR-4 is a path convention, so most failures come from a name that does not match its path. Check these rules when a class is “not found”:
- The namespace prefix (
Acme) maps to the base directory (src/). It is not repeated in the path. - Each remaining namespace segment becomes a directory.
AcmeControllerbecomesController/undersrc/. - The class name becomes the filename with a
.phpextension.HomeControllerisHomeController.php. - Case must match exactly.
Controller/andHomeController.phpcannot be spelledcontroller/orhomecontroller.php. A mismatch can work on a case-insensitive development filesystem and then fail on a case-sensitive Linux server, so test with the production casing. - Each file should declare one class whose namespace matches its directory.
Adding a new class under src/ with a correct namespace and path is enough. Under ordinary PSR-4 lookup you do not regenerate anything for a new file, because the autoloader resolves the path when the class is first used.
Controllers are ordinary mapped classes
From the autoloader’s point of view, a controller is just a class. Put it under a namespace and a path that follow the same mapping, such as AcmeControllerUserController in src/Controller/UserController.php. The autoloader does not know or care that it is a controller.
Routing, the request lifecycle, and how controllers receive input are not Composer or PSR-4 requirements. They are design decisions you make for your framework. This series handles them separately; the autoloader only answers “where is this class file?”
Keep the autoloader quiet: errors and exceptions
The PSR-4 specification sets a firm boundary for autoloader code. Its wording is: “Autoloader implementations MUST NOT throw exceptions, MUST NOT raise errors of any level, and SHOULD NOT return a value.” (PHP-FIG, PSR-4: Autoloader.)
That means the autoloader should silently do nothing when it cannot find a file, and let PHP report the missing class. Turning failures into a readable error is the application’s job, and it belongs at the boundary of your framework rather than inside the autoloader.
Rank #4
PHP’s set_error_handler page documents the function you use to route PHP warnings and notices through your own handler. A common pattern converts them into ErrorException so they can be handled like exceptions:
<?php
set_error_handler(function (int $severity, string $message, string $file, int $line): bool {
if (!(error_reporting() & $severity)) {
return false; // suppressed with @ or excluded by error_reporting()
}
throw new ErrorException($message, 0, $severity, $file, $line);
});
This is one reasonable boundary policy, not something Composer or PSR-4 prescribes. Decide what gets logged, what the user sees, and which HTTP status is returned in your framework’s own exception handling, and keep that logic out of the autoloader.
Development and production setups
Development: keep ordinary PSR-4 lookup
Ordinary PSR-4 lookup is the most convenient setup while you are building. New classes are found as soon as they exist at the right path, so you can keep writing code without rebuilding a class map. Composer’s schema documentation also recommends PSR-4 as its preferred mapping approach for ease of use.
Production: optimize the autoloader deliberately
Composer can optimize the autoloader for deployment by converting PSR-0 and PSR-4 rules into a classmap, which is a lookup table of class names to files. The standard optimization flag is composer dump-autoload --optimize (short form -o). The autoloader optimization article explains what changes, and the CLI command reference lists the options for dump-autoload.
Optimization is a deployment choice. Run it as part of your release step, not as a habit on every machine.
Classmap-authoritative mode is a stricter trade-off
The stricter option is composer dump-autoload --classmap-authoritative (short form -a). In this mode, when a class is absent from the classmap, Composer’s autoloader stops searching the PSR-4 rules. That is faster and predictable, but it breaks any code that creates classes at runtime, or any dependency that expects to load a file the map does not list. Use it only after you have confirmed that every class your application needs is present in the generated map.
What’s actually slowing this PC down?
Pick the symptom - the matching free tool is one click away.
Choosing a mapping method
PSR-4 covers most framework code. The other options exist for older or unusual layouts, and they differ in how much maintenance they require.
| Approach | Configuration | Best for | Trade-off |
|---|---|---|---|
| Ordinary PSR-4 lookup | "psr-4" in composer.json |
Day-to-day development and new framework code | New classes need only the correct path; lookup happens at runtime |
| Optimized classmap | composer dump-autoload --optimize |
Production deployments that want faster class lookup | Must be regenerated after class or mapping changes; the map is built from the current code |
| Authoritative classmap | composer dump-autoload --classmap-authoritative |
Production apps where every class is known in advance | No PSR-4 fallback for missing classes, which breaks runtime-generated classes |
| Classmap for legacy layouts | "classmap" in composer.json |
Older code that does not follow PSR-4 directory rules | Maintenance cost: the map must reflect the files it covers |
| Named files | "files" in composer.json |
Functions or other named files that cannot be autoloaded as classes | Files are included eagerly through the named list, so only list files that must load |
The schema reference for these keys is in the composer.json schema documentation.
Quick Recap
Common mistakes
- Class not found after adding a mapping. You skipped
composer dump-autoloadafter editingcomposer.json. The generated autoloader still holds the old mapping. - Class found on macOS or Windows but not on Linux. Directory or filename casing does not match the namespace or class name.
- Missing trailing separator in the prefix. A prefix without the trailing
can match sibling namespaces such asFooBarwhen you meantFoo. - Multiple includes of the autoloader. Include
vendor/autoload.phponce in the entry point. Scattering the include across files adds nothing and makes the bootstrap harder to follow. - Using authoritative mode with dynamic classes. Classes generated at runtime are absent from the map, and the autoloader will not fall back to PSR-4 for them.
Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.




