skip to content

Packagist

Packagist is the public registry Composer resolves PHP packages from, building versions from a VCS repository's tags and branches. Interviewers ask where dependencies come from and how to publish one.

on this pageshow

explore

questions

5

What is Packagist, and what does publishing a first PHP library to it involve?

level: juniorimportance: must knowfreq 55%

answer

  1. Composer's default repository
  2. vendor/name, lowercase
  3. submit the repository URL
  4. versions come from tags
  5. no version key in composer.json

basics

~20 s

Packagist is the public package registry Composer queries by default. To publish, push a repository with a valid composer.json named vendor/name, submit its URL on Packagist, and tag releases; Packagist reads versions from tags and branches.

solid answer

~40 s

Packagist (packagist.org) is Composer's default repository: anything published there installs with a plain `composer require vendor/name`, with no `repositories` entry. It does not host your code; it indexes a public VCS repository. To publish a library you give it a `composer.json` with a lowercase `vendor/name` `name`, a `description`, a `license` and its `require` and `autoload` sections, run `composer validate`, push it, and submit the repository URL on Packagist. Packagist then crawls the repository and builds one version per valid tag (`v1.0.0` is offered as `v1.0.0` and compared as `1.0.0`) and one development version per branch, such as `dev-main`. You should not put a `version` key in `composer.json`; the tags are the versions. Set up the code host's hook to Packagist so each push and tag is picked up promptly.

code

json · 14 lines
json
{
    "name": "mira/date-phrases",
    "description": "Turns timestamps into phrases such as '3 days ago'.",
    "type": "library",
    "license": "MIT",
    "require": {
        "php": ">=8.3"
    },
    "autoload": {
        "psr-4": {
            "Mira\\DatePhrases\\": "src/"
        }
    }
}

go deeper

for a junior

Know that Packagist is Composer's default registry, that packages are named vendor/name, and that releases are Git tags rather than a version key.

for a middle

Explain the publishing steps: composer validate, license and description, export-ignore in .gitattributes, submitting the URL, and the hook that keeps it current.

for a senior

Treat each tag as an immutable contract with consumers' lock files, fix mistakes with new versions, and plan how the package will be retired or replaced.

for a principal

Decide which internal code is worth publishing publicly: maintenance, support and security-disclosure commitments come with a public package.

## What Packagist is **Packagist** (packagist.org) is the main public **package repository** for Composer, and the only one Composer knows about without configuration. Composer adds it implicitly as the last entry in every project's repository list, which is why `composer require monolog/monolog` works with no `repositories` section at all. Packagist is a **metadata index**, not a code store. It reads packages from their **version control repositories** (Git, Mercurial, Subversion or Fossil). It records each version's `composer.json` data and where to download it, and serves that metadata to Composer clients. The code itself stays on the code host, and installs usually download a zip of the tagged commit from there. ## Preparing a first library Take a small date-formatting library that turns timestamps into phrases such as "3 days ago". Before submitting it: 1. **Name it** `vendor/name`, for example `mira/date-phrases`. The name must be lowercase, with words separated by `-`, `.` or `_`, and must match Composer's name pattern. `name` and `description` are required for a published package. 2. **Declare a license** with an SPDX identifier such as `MIT`, plus `require` (for example `"php": ">=8.3"`) and an `autoload` section so consumers can load the classes. 3. **Leave out `version`.** Composer and Packagist infer versions from tags. The schema docs warn that a hard-coded `version` conflicts with tag names; a tag whose version does not match it is skipped. 4. **Run `composer validate`.** It reports errors that make a `composer.json` unsuitable for publishing on Packagist, and by default it also errors if a `version` field is present. 5. **Trim the dist.** A `.gitattributes` file with `export-ignore` lines (`/tests export-ignore`, `phpunit.xml.dist export-ignore`) keeps tests and tooling out of the zip that consumers download for tagged releases. 6. **Push and tag**: commit, push, and create a tag such as `v1.0.0`. ## Submitting and keeping it current On packagist.org you sign in, choose **Submit**, and paste the public repository URL. Packagist crawls the repository, reads `composer.json` on the default branch to learn the package name, and then reads each tag and branch: | In the repository | On Packagist | |---|---| | tag `v1.0.0` or `1.0.0` | version named after the tag, compared as `1.0.0` (stable) | | tag `v1.1.0-RC1` | compared as `1.1.0-RC1` (stability RC) | | branch `main` | version `dev-main` | | branch `2.x` | version `2.x-dev` | After that, Packagist has to be told when the repository changes. The normal setup is a **hook**: the code host notifies Packagist on every push, so a new tag appears within moments. Without a hook, the package only changes when Packagist next re-crawls it, or when a maintainer triggers an update by hand, and users wonder why the release they were promised is missing. ## Common first-release mistakes - **Submitting before the first tag.** Packagist then lists only `dev-main`, and consumers with default stability settings cannot install anything. - **A vendor name that is somebody's personal account** for a library meant to outlive that person. Pick the vendor prefix the project will keep, because renaming later means publishing a new package and abandoning the old one. - **Tests, fixtures and CI files in the dist.** Without `export-ignore`, every consumer downloads them on every install. - **An over-tight `require`.** `"php": "8.5.*"` excludes every future PHP release; a lower bound such as `>=8.3` or a caret range is the usual choice for a library. - **No hook.** The first tag appears, the second does not, and the maintainer assumes the release process is broken. ## What publishing means for consumers - A **tag is a promise.** A consumer's lock file records the exact commit behind the version it installed. Moving or deleting a published tag breaks their reproducible installs, so fix mistakes with a new version instead. - The `composer.json` **at each tag** is what consumers get. A dependency constraint fixed on `main` after tagging `1.0.0` changes nothing for `1.0.0`. - Retiring the library later is done by **marking it abandoned**, optionally naming a replacement, not by deleting it. Publishing is free and public: anyone can require the package once it is listed, and Packagist serves its metadata to everyone. Code that must stay private belongs on a private repository instead.

  • Why should a library's composer.json not contain a version key?
    Packagist and Composer derive versions from VCS tags. A hard-coded `version` has to be edited by hand on every release, and when it disagrees with a tag, Composer skips that tag as a version that does not match `composer.json`. `composer validate` errors on the field by default for this reason.
  • You tagged 1.0.0 with a wrong dependency constraint. How do you fix it?
    Tag a new release, 1.0.1, with the corrected `composer.json`. Consumers may already have 1.0.0 in their lock files, pinned to that commit, so moving or deleting the tag breaks their reproducible installs. Each tag's `composer.json` is fixed once published.

Packagist is a library catalogue, not the shelves: it records every edition (tag) and where to find it, while the books stay on the code host that holds them.

saying these in an interview costs you the question

  • Packagist stores and serves a copy of the library's source code.
  • You set the release version in composer.json's version key.
  • Package names may use capital letters, like Mira/DatePhrases.
  • Packagist picks up new tags instantly without any hook.
  • Re-pointing a published tag is a safe way to fix a release.
open as a page

You pushed tag v1.3.0 of a PHP library on Packagist, but consumers' composer update does not offer 1.3.0; what do you check?

level: middleimportance: should knowfreq 30%

basics

~20 s

First check whether Packagist re-read the repository, usually a missing hook. Then check whether the tag was skipped for an invalid name, a mismatched version key or missing composer.json. Last, check the consumer's constraint, stability settings and repository order.

open as a page

How does Packagist turn a Git repository's tags and branches into Composer versions such as 1.2.0, 2.x-dev and dev-main?

level: middleimportance: should knowfreq 40%

basics

~20 s

Each valid tag becomes a release version, compared without its leading v, with any -beta or -RC suffix setting stability. Each branch becomes a dev version: 2.x becomes 2.x-dev, other names become dev-name, and extra.branch-alias can map dev-main to 1.0.x-dev.

open as a page

On Packagist, how do you retire a PHP package by marking it abandoned with a replacement, and what do consumers' Composer runs then show?

level: seniorimportance: should knowfreq 25%

basics

~10 s

Mark the package abandoned, optionally naming a replacement package, rather than deleting it. Existing versions keep installing, but Composer warns that the package is abandoned and suggests the replacement, and composer audit reports it.

open as a page

Packagist.org serves only public packages; how should a team distribute private PHP packages to its own projects instead?

level: seniorimportance: should knowfreq 32%

basics

~20 s

Keep private packages off Packagist.org and serve them from a private repository: individual vcs entries for a few packages, a static registry built with Satis, or a hosted private registry. Credentials go in auth.json or environment variables.

open as a page