In a composer.json, when do you use classmap or files autoloading instead of psr-4, and what does autoload-dev change?
answer
- classmap scans .php and .inc
- files load on every request
- functions cannot be autoloaded
- root files load last
- autoload-dev is root-only
basics
~20 sclassmap suits code that does not follow PSR-4: Composer scans directories at dump time, so new classes need dump-autoload. files includes function files on every request. autoload-dev holds test-only rules, is root-only and is skipped by --no-dev.
solid answer
~40 s`classmap` takes directories or files; at dump time Composer scans every `.php` and `.inc` file in them and records each class's path in `vendor/composer/autoload_classmap.php`. It suits legacy or generated code whose file layout does not match its namespaces, and the cost is that new classes need `composer dump-autoload`. `files` is for code PHP cannot autoload at all, mainly **plain functions** and constants: each listed file is included **every time** `vendor/autoload.php` is included, dependencies first and the root package last, so keep them small. `autoload-dev` takes the same keys for test and tooling code; it is **root-only**, so a library's test classes never reach consumers, and `--no-dev` installs leave it out of the generated autoloader. `exclude-from-classmap` keeps paths such as `/Tests/` out of any classmap, including an optimized one.
code
json · 10 lines{
"autoload": {
"psr-4": { "Shop\\": "src/" },
"classmap": ["legacy/"],
"files": ["src/helpers.php"]
},
"autoload-dev": {
"psr-4": { "Shop\\Tests\\": "tests/" }
}
}go deeper
Know that functions go in files, legacy classes can use classmap, and tests go in autoload-dev.
Explain that classmap is built by scanning at dump time, files load on every request in dependency order, and autoload-dev is root-only and skipped by --no-dev.
Keep files entries side-effect free, exclude test paths from classmaps, and migrate classmap code toward PSR-4 when it is touched.
Decide how far to invest in moving a legacy codebase from classmap to PSR-4, weighing rebuild friction and ambiguity risks against migration cost.
## Four ways to tell Composer where code lives Composer's `autoload` section accepts `psr-4`, the legacy `psr-0`, `classmap` and `files`. PSR-4 is the default choice because it needs no rebuild when you add classes. The other two exist for code that PSR-4 cannot describe. | Key | Resolves | Rebuild when adding code? | Cost per request | |---|---|---|---| | `psr-4` | class name → path by prefix | no | a filesystem check on first use of each class | | `classmap` | class name → recorded path | yes | an array lookup | | `files` | not by name: always included | yes, when adding a file | every listed file is loaded, used or not | ## `classmap`: for code that does not follow PSR-4 ```json { "autoload": { "classmap": ["legacy/", "lib/Payment.php", "modules/*/lib/"] } } ``` During `install`, `update` or `dump-autoload`, Composer **scans** every `.php` and `.inc` file in the listed paths, finds each class-like declaration, and records its path in `vendor/composer/autoload_classmap.php`. A `*` in a path matches any directory name. Use it for: - legacy code with several classes per file, or file names unrelated to class names; - generated code that lands in one directory; - a library that simply prefers an explicit map. The price: a class added later is unknown until `composer dump-autoload` rescans. If two files declare the same class, Composer warns about an ambiguous class resolution. ## `files`: for what PHP cannot autoload PHP's autoloading works only for **classes, interfaces, traits and enums**. A namespaced **function** or a constant defined with `define()` has no autoload hook, so a package that ships helper functions lists their file under `files`: ```json { "autoload": { "files": ["src/functions.php"] } } ``` Every `files` entry is included **whenever `vendor/autoload.php` is included**, right after the class loader is registered. Order follows the dependency graph: if package A depends on B, B's files load first; ties are alphabetical; the **root package's files load last**. So you cannot use `files` to override a dependency's function; the documentation suggests including your own file *before* `vendor/autoload.php` for that. Because they run on every request, `files` should declare functions and nothing else: no database connections, no configuration loading, no output. ## `autoload-dev`: rules for development only `autoload-dev` accepts the same keys and holds rules for test suites, fixtures and dev tooling: 1. It is **root-only**. Composer reads it only from the project where it runs, so a library's test namespaces are never registered in a consumer's autoloader. 2. It is **skipped** when the autoloader is generated with `--no-dev`, as in `composer install --no-dev`. `dump-autoload` infers dev mode from the last `install` or `update`, and accepts `--dev` or `--no-dev` to override it. 3. Keeping tests out of `autoload` keeps production's maps smaller and stops test classes from being loadable in production. ## Keeping paths out of a classmap `exclude-from-classmap` lists paths, relative to the package root, that the classmap generator ignores, with `*` and `**` wildcards: ```json { "autoload": { "exclude-from-classmap": ["/tests/", "/legacy/fixtures/"] } } ``` It also applies when an optimized autoloader converts PSR-4 rules into a classmap, which is its most common use. ## A legacy migration pattern A common real situation is an older application whose classes live in `lib/` with names like `Shop_Order_Invoice` and no namespaces. A gradual migration looks like this: - keep `"classmap": ["lib/"]` so the old classes keep loading; - add `"psr-4": {"Shop\\": "src/"}` for all new, namespaced code; - move classes from `lib/` to `src/` one area at a time, adding namespaces as you go; - remember that every class added to `lib/` in the meantime needs `composer dump-autoload`, which is the friction that motivates finishing the move. Both rule types coexist in one autoloader, and the classmap is always consulted first. ## Choosing 1. New code: `psr-4`. 2. Helper functions: `files`, one small file per package. 3. Code that cannot follow PSR-4: `classmap`, and remember to dump. 4. Anything test-related: `autoload-dev`.
- A package lists a 2,000-line bootstrap file under files. Why is that a problem even if the application never calls it?Every `files` entry is included each time `vendor/autoload.php` is included, which is every web request and every CLI run. The file is parsed and executed whether or not anything uses it, and any side effects run too. `files` should contain small function declarations only.
- Can you override a dependency's helper function by declaring the same function in your own files entry?No. Root `files` are included last, after dependencies, so the dependency's function is already declared and a second declaration is a fatal error. Composer's documentation suggests including your own file before `vendor/autoload.php` if you really need to define the function first.
saying these in an interview costs you the question
- Namespaced functions can be autoloaded with psr-4 like classes
- files entries are loaded lazily, only when a function from them is called
- A library's autoload-dev rules are registered in the projects that install it
- Classes added to a classmap directory are found without dump-autoload
- The root package's files are loaded first so they can override dependencies