What is Packagist, and what does publishing a first PHP library to it involve?
answer
- Composer's default repository
- vendor/name, lowercase
- submit the repository URL
- versions come from tags
- no version key in composer.json
basics
~20 sPackagist 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 sPackagist (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{
"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
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.
Explain the publishing steps: composer validate, license and description, export-ignore in .gitattributes, submitting the URL, and the hook that keeps it current.
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.
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.