In a composer.json, how do the vcs, path and composer repository types differ, and when would you choose each?
answer
- where package metadata comes from
- fork branch required as dev-bugfix
- path: symlinked or mirrored
- packages.json on a registry
- Composer 2: repositories are canonical
basics
~20 sA 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.
solid answer
~40 sAll three are entries in the root `repositories` array and change where Composer looks for a package. A `composer` repository points at a registry that publishes a `packages.json` index; Packagist is one and is added implicitly as the last repository. A `vcs` repository points at a Git, Mercurial, Subversion or Fossil URL and derives versions from its tags and branches, so it suits a patched fork: `"require": {"monolog/monolog": "dev-bugfix"}` with the package name unchanged. A `path` repository points at a local directory, is symlinked when possible or mirrored otherwise, and suits monorepo packages. In Composer 2 every repository is canonical: once a package is found in one, lower repositories are not consulted for it, and `only`/`exclude` narrow what a repository may supply. Repositories declared by dependencies are never loaded.
code
json · 23 lines{
"repositories": [
{
"type": "composer",
"url": "https://packages.example.internal",
"only": ["acme/*"]
},
{
"type": "vcs",
"url": "https://git.example.internal/forks/monolog"
},
{
"type": "path",
"url": "../../packages/*",
"options": {"symlink": false}
}
],
"require": {
"acme/billing": "^3.2",
"monolog/monolog": "dev-bugfix",
"acme/shared-kernel": "*"
}
}go deeper
Know that Packagist is the default and that the repositories key adds other sources. Recognise the three types: composer, vcs and path.
Explain how a fork is used through a vcs repository with a dev- branch constraint, and why a path repository is symlinked or mirrored.
Explain canonical lookup in Composer 2, how only and exclude narrow a repository, and why internal packages should come from a registry placed above Packagist.
Weigh a private registry against many vcs entries across dozens of projects: resolution speed, one place to control access, and a single list to audit.
## What a repository is to Composer A **repository** is a source of package metadata: a list of package names, versions and where to download each one. By default Composer knows exactly one, **Packagist.org**. Every other source is declared in the root `composer.json` under `repositories`, and only the **root** package's list counts. Repositories that a dependency declares in its own `composer.json` are ignored, so a private package that itself depends on another private package forces the root project to declare both. ## The three types that matter | Type | Points at | Versions come from | Typical use | |---|---|---|---| | `composer` | a URL serving `packages.json` | the registry's metadata | Packagist, a self-hosted or private registry | | `vcs` | a Git, Mercurial, Subversion or Fossil repository | tags (releases) and branches (`dev-<branch>`) | a patched fork, a private repository without a registry | | `path` | a local directory, absolute or relative, globs allowed | the checked-out branch or tag, the package's `version` key, else `dev-master` | packages developed inside the same monorepo | ### composer The `composer` type is the registry protocol. You give the base URL, and Composer fetches the `packages.json` index and the per-package metadata files it points to. It is fast because the registry has already read every package's `composer.json`, and Composer usually downloads zip dists rather than cloning. ### vcs The `vcs` type makes Composer clone or query the repository itself and read `composer.json` on each tag and branch. The classic use is a **fork with a fix**: 1. Push the fix to a branch named `bugfix` on your fork. 2. Add the fork as a `vcs` repository. 3. Require the package under its **original** name with the constraint `dev-bugfix`. The package's `name` in the fork must stay the same as the original, or the override does not apply. If other dependencies require a tagged range of the same package, an inline alias such as `dev-bugfix as 1.0.x-dev` lets the branch stand in for that version line. The cost is speed: each VCS repository has to be scanned when Composer builds its package pool, which is why many teams move private packages behind a `composer` registry once there are more than a handful. ### path The `path` type turns a local directory into a package. Composer **symlinks** it into `vendor/` when it can (the console prints `Symlinking from ...`) and **mirrors** (copies) it otherwise. `"options": {"symlink": false}` forces a copy, which is what you want when building a deployable artifact from a monorepo, since a symlink pointing outside the build directory breaks once the artifact moves. `"options": {"versions": {"my/package": "4.2-dev"}}` sets the version when it cannot be inferred. ## Priority: canonical repositories and filters Composer searches repositories **top to bottom**, with Packagist added implicitly **last**. In Composer 2 every repository is **canonical** by default: once a package name is found in a repository, lower repositories are not searched for that name, even if they carry a higher version. Composer 1 did the opposite. This is why a fork declared as a `vcs` repository overrides the Packagist original, and why an internal package name cannot be replaced by a higher version published on Packagist. Three settings adjust this: - `"canonical": false` lets Composer keep looking in lower repositories and pick the best version across them. - `"only": ["acme/*"]` restricts a repository to matching package names. - `"exclude": ["toy/package"]` stops it supplying the named packages. `{"packagist.org": false}` in the list, or `composer config -g repo.packagist.org false`, removes Packagist entirely for projects that must install only from an internal registry. ## Settings that keep repositories honest - `secure-http` defaults to `true`, so Composer refuses plain `http://` URLs for downloads; turning it off to reach an internal server is almost always the wrong fix compared with giving that server a certificate. - Credentials for private `composer` and `vcs` repositories belong in `auth.json` or environment variables, never inside the `url` in `composer.json`, which is committed. - A `path` repository only works where the directory exists, so a project that ships to a server needs the path packages inside the build context, mirrored rather than symlinked. ## Choosing - Private code shared by several projects: a `composer` registry, restricted with `only` to your vendor prefix. - A temporary patch to an open-source library: a `vcs` repository pointing at your fork, and a plan to go back upstream. - Several packages developed in one repository: `path` repositories with a glob, mirrored for release builds.
- Your fork is declared as a vcs repository, but Composer still installs the Packagist version. What do you check?Check that the fork's `composer.json` keeps the original `name`; a renamed package is a different package. Check that the constraint names the branch with the `dev-` prefix, such as `dev-bugfix`, and that the repository appears in the root `composer.json`, not in a dependency's. If another package requires a tagged range, add an inline alias such as `dev-bugfix as 1.0.x-dev`.
- Why can't a private package declare the repositories of its own private dependencies?Composer loads repositories from the root package only. Loading them recursively would mean discovering new sources while resolving, which is slow and could pull a package from a source the root project never chose. The root `composer.json` has to list every repository, or point at one registry that serves all the private packages.
saying these in an interview costs you the question
- Repositories declared in a dependency's composer.json are loaded automatically.
- Composer 2 always picks the highest version found across all repositories.
- Renaming the package in a fork is how you make Composer prefer it.
- A path repository always copies the directory into vendor.
- Packagist is searched before any custom repository.