skip to content

Composer

PHP's dependency manager: the composer.json manifest, the composer.lock it installs from, the autoloader it generates, and its scripts and audits. Interviewers ask because every PHP project uses it.

on this pageshow

explore

questions

20

In a composer.json, how do you configure psr-4 autoloading for your own code, and what does requiring vendor/autoload.php give you?

level: juniorimportance: must knowfreq 62%

answer

  1. namespace prefix to directory
  2. prefix ends with two backslashes in JSON
  3. require vendor/autoload.php once
  4. new prefix needs dump-autoload
  5. new class under a prefix does not

basics

~20 s

Map a namespace prefix to a directory under autoload.psr-4, for example "App\": "src/", then require vendor/autoload.php once at the entry point. That file registers Composer's class loader for your code and every dependency, and includes any files entries.

solid answer

~40 s

In `composer.json` you add `"autoload": {"psr-4": {"App\\": "src/"}}`: a namespace **prefix**, ending in a backslash (written `\\` in JSON), mapped to a directory relative to the package root. A prefix can map to several directories as an array, and an empty prefix `""` makes a fallback directory. Composer merges your rules with every installed package's rules into generated files under `vendor/composer/` and writes `vendor/autoload.php`. Requiring that file once, in the front controller or CLI entry script, registers Composer's `ClassLoader`, includes any `files` entries and returns the loader, so `new App\Billing\Invoice()` just works. After **adding or changing a mapping** you run `composer dump-autoload`; after adding a **class** under an existing PSR-4 prefix you do not, because the default loader looks the file up on disk.

code

json · 8 lines
json
{
    "autoload": {
        "psr-4": { "App\\": "src/" }
    },
    "autoload-dev": {
        "psr-4": { "App\\Tests\\": "tests/" }
    }
}

go deeper

for a junior

Know the psr-4 key format, that vendor/autoload.php is required once at the entry point, and when composer dump-autoload is needed.

for a middle

Explain what autoload.php registers and includes, why new PSR-4 classes need no rebuild, and why the trailing separator matters.

for a senior

Keep mappings minimal and case-correct, keep tests in autoload-dev, and catch mapping errors in CI before a case-sensitive server does.

for a principal

Set namespace and directory conventions for a codebase so autoloading stays predictable as teams and packages multiply.

## What Composer's autoloader is PHP loads a class definition the first time code uses a class it has not seen, by calling the autoloaders registered with the engine. **Composer generates one** for you: it reads the `autoload` rules of your project and of every installed package, writes them into PHP files under `vendor/composer/`, and gives you one entry point, `vendor/autoload.php`. How PHP's autoload hook works and how the PSR-4 standard turns a class name into a path are the language's and the standard's business; this question is about Composer's side. ## Configuring `psr-4` ```json { "autoload": { "psr-4": { "App\\": "src/", "App\\Legacy\\": ["lib/", "legacy/"] } } } ``` - The **key** is a namespace prefix. It must end with a namespace separator, written `\\` in JSON because the backslash is JSON's escape character. The trailing separator stops `Foo\` from also matching `FooBar\`. - The **value** is a directory relative to the package root, or an array of directories searched in order. - The prefix is **not** part of the path: `App\Billing\Invoice` under `"App\\": "src/"` lives in `src/Billing/Invoice.php`. - An **empty prefix** `"": "src/"` makes `src/` a fallback for any namespace, rarely a good idea in an application. - Composer also supports the older `psr-0` key; new code uses `psr-4`. All PSR-4 rules from all packages are merged into `vendor/composer/autoload_psr4.php` during `install`, `update` or `dump-autoload`. ## What `vendor/autoload.php` does Including it: 1. runs the runtime platform check first, when one was generated; 2. registers Composer's `ClassLoader` with the PHP engine, loaded with the merged `psr-4`, `classmap` and legacy `psr-0` rules; 3. includes every `files` entry, dependencies first, the root package last; 4. **returns the `ClassLoader` instance**, so a test bootstrap can add rules at runtime with `$loader->addPsr4('App\\Tests\\', __DIR__)`. You require it **once**, as early as possible: in the web front controller (`public/index.php`) and in each CLI entry script. Libraries never include it themselves; the application that installs them does. ## When you must regenerate | Change | `composer dump-autoload` needed? | |---|---| | add a class under an existing PSR-4 prefix | no, found on disk at first use | | add or change a `psr-4` prefix or directory | yes | | add a class in a `classmap` directory | yes | | add a `files` entry | yes | | install, update or remove a package | no, those commands regenerate it | That first row is PSR-4's main convenience, and the reason Composer's documentation recommends it: new classes need no rebuild during development. It is also why an **optimized** production autoloader behaves differently, a separate topic. ## Common mistakes - Writing a single backslash at the end of the JSON key: the backslash escapes the closing quote, the JSON no longer parses, and Composer reports a parse error. - Forgetting the trailing separator, so the key reads `"App": "src/"` instead of `"App\\": "src/"`; Composer rejects it with an error that psr-4 namespaces must end with a namespace separator. - Mismatched case between the namespace and the directory: it works on a case-insensitive filesystem on a laptop and fails on a Linux server. - Adding a new prefix and forgetting `dump-autoload`, then blaming the namespace. - Including `vendor/autoload.php` from many files instead of once at the entry point. ## What the generated files are Looking inside `vendor/composer/` demystifies the whole mechanism. After a dump you will find, among others: - `autoload_psr4.php`: an array from each PSR-4 prefix to its directories, merged across all packages; - `autoload_classmap.php`: an array from class name to file, including Composer's own classes and anything from `classmap` rules; - `autoload_files.php`: the ordered list of `files` entries, when any package declares them; - `autoload_real.php` and `autoload_static.php`: the bootstrap that builds the `ClassLoader` from those arrays; - `ClassLoader.php`: the loader class itself. All of them are generated: never edit them by hand, and never commit `vendor/` to fix an autoload problem. Change `composer.json` and dump again. ## Where test code goes Test namespaces belong in `autoload-dev`, which uses the same syntax but is root-only and left out of `--no-dev` installs, so production's autoloader does not carry test classes.

  • You added src/Billing/Invoice.php under an existing App\ prefix and the class is found without any Composer command. Why?
    With the default, unoptimized autoloader, a PSR-4 rule is resolved at runtime: the loader turns the class name into a path under `src/` and checks whether the file exists. Nothing needs to be regenerated for a new class. You need `composer dump-autoload` only when the mapping itself changes, or when the autoloader was dumped as an authoritative classmap.
  • What does require 'vendor/autoload.php' return, and when is that useful?
    It returns Composer's `ClassLoader` instance. A test bootstrap or a tool can call methods on it such as `addPsr4()` to register extra prefixes at runtime without touching `composer.json`.

saying these in an interview costs you the question

  • You must run composer dump-autoload every time you add a new class under a PSR-4 prefix
  • Every PHP file should require vendor/autoload.php at the top
  • A PSR-4 prefix key without a trailing separator works the same as one with it
  • A library should require its own vendor/autoload.php so it works when installed
  • The namespace prefix is repeated as a directory inside the mapped path
open as a page

In Composer, what is the difference between composer install and composer update, and which one should a deploy run?

level: juniorimportance: must knowfreq 75%

basics

~10 s

composer install installs the exact versions in composer.lock; composer update re-resolves composer.json's constraints, rewrites composer.lock with the newest allowed versions, then installs. Deploys and CI run install, production with --no-dev; developers run update deliberately.

open as a page

In a composer.json, what is the difference between require and require-dev, and when are require-dev packages not installed?

level: juniorimportance: must knowfreq 70%

basics

~20 s

require lists packages the code needs at runtime, installed everywhere and passed on to the package's consumers. require-dev lists test and tooling packages; it is root-only, so a dependency's dev list is never installed, and --no-dev builds skip it.

open as a page

In Composer, what does composer dump-autoload -o change about how classes are found, and why is it for production rather than development?

level: middleimportance: must knowfreq 55%

basics

~20 s

dump-autoload -o scans PSR-4 and PSR-0 directories and writes every class into the classmap, so known classes resolve by array lookup, not a filesystem check. It suits production, where code changes only at deploy, not development.

open as a page

In a composer.json constraint, how does ^1.2 differ from ~1.2, and how do ^1.2.3, ~1.2.3 and ^0.3 behave?

level: middleimportance: must knowfreq 65%

basics

~10 s

In Composer, ^1.2 and ~1.2 are the same range, >=1.2.0 <2.0.0. They differ with three parts: ~1.2.3 means >=1.2.3 <1.3.0 while ^1.2.3 means >=1.2.3 <2.0.0. For 0.x, ^0.3 means >=0.3.0 <0.4.0.

open as a page

Your CI pipeline must fail whenever a Composer dependency has a known security advisory; how do composer audit and config.policy blocking cover that in Composer 2.10?

level: seniorimportance: must knowfreq 45%

basics

~20 s

Run composer audit, which exits 1 when locked or installed packages match an advisory, as a CI step and on a schedule. Update-time blocking stops new vulnerable versions entering the lock, but composer install from an existing lock does not check advisories.

open as a page

In a composer.json, what does the scripts section do, and when do post-install-cmd and post-autoload-dump fire?

level: juniorimportance: should knowfreq 48%

basics

~20 s

The scripts section maps Composer event names, or custom command names, to shell commands and static PHP callbacks. post-install-cmd runs after install with a lock file present; post-autoload-dump runs after every autoloader dump, including dump-autoload.

open as a page

In a composer.json, when do you use classmap or files autoloading instead of psr-4, and what does autoload-dev change?

level: middleimportance: should knowfreq 40%

basics

~20 s

classmap 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.

open as a page

In Composer, what is the content-hash in composer.lock, which composer.json changes alter it, and what does composer install do when it no longer matches?

level: middleimportance: should knowfreq 42%

basics

~20 s

content-hash is an md5 of the composer.json keys that affect resolution, such as require, repositories and config.platform. On a mismatch, composer install warns and installs the locked versions; Composer 2.10 errors if a required package is missing or unsatisfied.

open as a page

In Composer, why can composer update vendor/package fail, and how do -w, -W and --minimal-changes change which packages it may touch?

level: middleimportance: should knowfreq 40%

basics

~20 s

composer update vendor/package frees only that package; its dependencies stay locked, which can block a newer release. -w also frees its non-root dependencies, -W root ones too, and --minimal-changes (2.7+) moves transitive packages only where required.

open as a page

In a composer.json, how do minimum-stability, prefer-stable and a stability flag like @beta decide whether Composer may install an unstable release?

level: middleimportance: should knowfreq 40%

basics

~20 s

minimum-stability, default stable, filters out less stable releases for all packages. A flag such as ^2.0@beta relaxes that for one package. prefer-stable true picks a stable release whenever one fits. All three are read only from the root composer.json.

open as a page

In a composer.json, how do the vcs, path and composer repository types differ, and when would you choose each?

level: middleimportance: should knowfreq 38%

basics

~20 s

A composer repository serves prebuilt metadata from a packages.json, like Packagist or a private registry. A vcs repository reads composer.json from a Git or other VCS repo's branches and tags. A path repository links a local directory, typically in a monorepo.

open as a page

A large PHP e-commerce codebase still spends noticeable request time in Composer's autoloader after -o; how do --classmap-authoritative and --apcu-autoloader compare, and what can each break?

level: seniorimportance: should knowfreq 32%

basics

~20 s

--classmap-authoritative (-a) treats the classmap as complete, so misses return instantly, but classes generated or added after the dump never load. --apcu-autoloader caches every lookup, hit or miss, in APCu, needs the extension and a fresh prefix per deploy. Pick one.

open as a page

A PHP deploy ended up running a different version of a Composer package than CI tested; which Composer workflow mistakes cause this, and how do you prevent them?

level: seniorimportance: should knowfreq 45%

basics

~20 s

Something resolved versions instead of replaying one lock: an uncommitted composer.lock, a deploy or CI script running composer update, or a badly merged lock. Commit the lock, run composer validate in CI, and deploy with composer install --no-dev.

open as a page

Your team runs PHP 8.5 locally but production runs 8.3; how do composer.json's php and ext-* requirements and config.platform keep composer update from locking incompatible packages?

level: seniorimportance: should knowfreq 38%

basics

~20 s

php and ext-* in require are platform packages: Composer installs nothing, only checks the running PHP. Setting config.platform.php to production's version, such as 8.3.12, makes composer update resolve as if on that PHP, so nothing needing 8.4 gets locked.

open as a page

In a Composer library's composer.json, how do you declare support for both major versions 1 and 2 of a dependency, and what mistakes break consumers?

level: seniorimportance: should knowfreq 30%

basics

~20 s

Write an OR constraint such as "acme/serializer": "^1.8 || ^2.0", with each lower bound the oldest release you test. Avoid >=1.8, which also accepts 3.0, and exact pins, which leave consumers' solvers no room; test the lowest and highest resolutions in CI.

open as a page

In Composer, what does config.allow-plugins control, and why does a newly added plugin fail a non-interactive CI install?

level: seniorimportance: should knowfreq 40%

basics

~10 s

allow-plugins lists which composer-plugin packages may execute code inside Composer. It defaults to empty, so an unlisted plugin triggers a prompt interactively and, with --no-interaction in CI, a blocked-plugin error that fails the command.

open as a page

In Composer 2.10, composer update is blocked by an advisory you assessed as not affecting you; how do you configure config.policy to proceed without hiding it?

level: seniorimportance: should knowfreq 28%

basics

~10 s

Add the advisory's ID under config.policy.advisories.ignore-id with a reason and on-audit set to false. Updates may then select the version, composer audit still reports it, and blocking stays on for every other advisory.

open as a page

In Composer, what does the generated vendor/composer/platform_check.php do, and how does the platform-check config setting change it?

level: seniorimportance: nice to knowfreq 18%

basics

~20 s

platform_check.php runs when vendor/autoload.php is included and stops the request if the running PHP is older than the installed packages require. platform-check defaults to php-only; true also checks that required extensions are loaded; false skips generating the file.

open as a page

In Composer, how do composer why and composer why-not help when an upgrade to a new major version is blocked?

level: seniorimportance: nice to knowfreq 28%

basics

~20 s

composer why vendor/pkg (alias of depends) lists the installed packages that require it and their constraints. composer why-not vendor/pkg 3.0 (alias of prohibits) lists what blocks that version, and works for php too. -t shows a tree.

open as a page