Composer and Packages
|
This content was generated with the assistance of AI and should be verified against the official documentation before being relied on in production. The reference material consulted while preparing these pages is listed in the section bibliography. |
This page is written against Composer 2.10 (the latest 2.x release, 2.10.3) on PHP 8.5, checked on 2026-10-05. Composer installs the libraries a project depends on, resolves compatible versions of all of them, records the exact result in a lock file, and generates an autoloader. Every modern PHP project, framework and tool — Laravel, Symfony, PHPUnit, PHPStan — is installed with it, and its public registry, Packagist, hosts the ecosystem’s packages.
Installing Composer and starting a project
Composer is a single PHAR. Install it with the instructions on getcomposer.org, with php.new or Herd, from the
composer Docker image, or through the operating system’s package manager. composer init creates a composer.json
interactively; composer require adds a dependency and creates the file if needed.
composer --version # Composer version 2.10.x
composer init --name=acme/library-catalogue --type=project --no-interaction
composer require symfony/console # adds "symfony/console": "^8.1" and installs it
composer require --dev phpunit/phpunit phpstan/phpstan
composer.json
The project’s composer.json declares what it needs; Composer works out everything else:
{
"name": "acme/library-catalogue",
"description": "Library catalogue tools",
"type": "project",
"license": "MIT",
"require": {
"php": ">=8.4",
"ext-intl": "*",
"ext-pdo": "*",
"symfony/console": "^8.1"
},
"require-dev": {
"phpunit/phpunit": "^13.0",
"phpstan/phpstan": "^2.2"
},
"autoload": {
"psr-4": { "Library\\": "src/" }
},
"autoload-dev": {
"psr-4": { "Library\\Tests\\": "tests/" }
},
"scripts": {
"test": "phpunit",
"analyse": "phpstan analyse src --level=8",
"check": ["@analyse", "@test"]
},
"config": {
"sort-packages": true,
"optimize-autoloader": true
},
"minimum-stability": "stable"
}
The main keys:
-
name(vendor/package),type(library,project,metapackage,composer-plugin,php-extfor PIE),license; -
require— what is needed in production, including the PHP version and extensions (ext-intl);require-dev— tools for development and tests, skipped with--no-dev; -
autoload/autoload-dev— class loading rules (below); -
scripts— named commands run withcomposer <name>, plus hooks such aspost-autoload-dump; -
config— Composer settings for this project:platform,sort-packages,optimize-autoloader,policy; -
repositories— additional package sources; -
minimum-stability/prefer-stable— whether non-stable releases may be installed; -
conflict,replace,provide,suggest,extra— advanced relationships and plugin configuration.
Source: The composer.json schema
Version constraints
Packages follow (or should follow) semantic versioning: MAJOR.MINOR.PATCH, where only a new major version may break
compatibility. Constraints describe which versions are acceptable:
| Constraint | Meaning | Example |
|---|---|---|
|
caret: compatible versions, up to the next major |
|
|
caret below 1.0 is narrower: minor versions may break |
|
|
tilde: last given digit may increase |
|
|
wildcard |
|
|
ranges, AND (space or comma) and OR ( |
|
|
exact version (avoid in libraries) |
|
|
a branch; a stability flag on one constraint |
Use ^ for almost everything. minimum-stability (stable by default; RC, beta, alpha, dev) sets the lowest
stability allowed for all packages, and prefer-stable: true picks stable releases when both are possible.
Source: Versions and constraints
composer.lock: install versus update
Resolving constraints produces a lock file, composer.lock, with the exact version, source and checksum of every
installed package, direct and transitive. The two commands that install packages treat it differently:
-
composer installinstalls exactly what the lock file says (or resolves and writes it if there is none). It is reproducible: CI, staging, production and every developer get the same versions. -
composer updatere-resolves the constraints, upgrades within them, and rewrites the lock file. Run it deliberately, ideally for specific packages (composer update symfony/console), and review the diff.
Commit composer.lock for applications. Libraries usually commit it too, for reproducible development, but it is
ignored when the library is installed as a dependency. Never commit vendor/.
constraints: ^8.1"] -->|composer update| R{"dependency
resolver (SAT)"} P[("Packagist and
other repositories")] --> R R --> L["composer.lock
exact versions + hashes"] L -->|composer install| V["vendor/
downloaded packages"] V --> G["vendor/autoload.php
generated autoloader"] A -->|autoload rules| G G -->|require once| APP["your application
bin/, public/index.php"]
composer install --no-dev --optimize-autoloader --no-interaction # production / CI deploy
composer update symfony/console --with-dependencies # upgrade one package and its deps
composer outdated --direct # what could be upgraded?
composer why symfony/string # who requires this package?
composer why-not phpunit/phpunit 14.0 # what prevents an upgrade?
composer show --tree # the dependency tree
Source: Basic usage: installing dependencies and CLI: update
Autoloading
Composer generates vendor/autoload.php, which every entry point requires once. Autoload rules come from your own
composer.json and from every installed package:
-
psr-4— a namespace prefix mapped to a directory (the standard; see Namespaces and Autoloading); -
classmap— scan directories or files and map every class found (legacy code, generated code); -
files— files always included (helper functions, which cannot be autoloaded); -
exclude-from-classmap— paths to skip.
In production, generate an optimised autoloader: composer dump-autoload --optimize (-o) converts PSR-4 rules
into a class map, avoiding filesystem lookups; --classmap-authoritative (-a) additionally tells the autoloader
not to look for classes outside the map; --apcu caches lookups in APCu.
$ composer dump-autoload --classmap-authoritative
Generated optimized autoload files (authoritative) containing 216 classes
Source: Schema: autoload and Autoloader optimization
Platform requirements
php and ext-* entries in require declare what the runtime needs. Composer refuses to install on a PHP that does
not satisfy them, which turns "Call to undefined function intl_…" in production into an installation error.
composer check-platform-reqs verifies a machine; config.platform pins a fake platform (for example the production
PHP version) when resolving on a different machine, so that the lock file never contains packages the server cannot
run:
{
"config": {
"platform": { "php": "8.4.26" }
}
}
--ignore-platform-reqs exists for exceptional cases (building in a container without the extensions); do not make it
a habit.
Source: Platform packages and Config: platform
Scripts
Scripts give a project a uniform command vocabulary (composer test, composer check) and run on events such as
post-install-cmd or post-autoload-dump. @other-script references another script, @php runs the current PHP
binary, and vendor/bin is on the PATH while scripts run.
composer test # runs "phpunit" from vendor/bin
composer check # runs @analyse then @test
composer run-script --list
Laravel’s skeleton uses scripts for composer run dev (starting the server, queue worker, log viewer and Vite
together) and to run artisan package:discover after autoload dumps.
Source: Scripts
Repositories
Packagist is the default repository. Others can be added under repositories:
-
vcs— a Git repository (private packages, forks); -
path— a local directory, symlinked intovendor/(developing a package and an application together); -
composer— a private Composer repository (Private Packagist, Satis, a self-hosted registry); -
package— a package defined inline.
{
"repositories": [
{ "type": "vcs", "url": "https://github.com/acme/marc-parser" },
{ "type": "path", "url": "../packages/catalogue-core" }
],
"require": {
"acme/marc-parser": "^1.2",
"acme/catalogue-core": "@dev"
}
}
Authentication for private repositories goes in auth.json (or the COMPOSER_AUTH environment variable), never in
composer.json.
Source: Repositories
Security: audit, dependency policies and bump
composer audit checks the installed packages against security advisories (from Packagist’s advisory database by
default) and also reports abandoned packages and packages flagged as malware; it exits with a non-zero code when it
finds problems, which makes it a natural CI step. Since Composer 2.9, updates also block installing package
versions with known advisories during update, require and remove, configured since 2.10 through the unified
config.policy setting (which replaces most of the older config.audit keys).
$ composer audit --locked
No security vulnerability advisories found.
$ composer bump --dev-only # raise lower bounds in require-dev to the installed versions
composer bump raises the lower bound of each constraint to the currently installed version (^8.0 becomes ^8.1.8),
which documents what you actually tested; do not run it on the require section of a library, as it narrows what
users can install.
Source: CLI: audit, Config: policy and CLI: bump
Packagist and publishing a library
To publish a library: give it a name, a license, PSR-4 autoloading and accurate require constraints; push it to a
public Git repository; submit the repository URL on packagist.org; and tag releases following semantic versioning
(git tag v1.2.0). Packagist updates through a webhook. Keep library constraints wide ("php": "^8.3",
"psr/log": "^2.0 || ^3.0"), test against the lowest and highest versions (composer update --prefer-lowest), and
never require dev tools in require.
Source: Libraries and About Packagist
PIE for extensions
Composer installs PHP code, not C extensions. PIE, the PHP Installer for Extensions, does the same job for
extensions, reading packages of type php-ext from Packagist; run in a project, pie install installs the missing
extensions that composer.json requires. See Configuration
and Extensions.
Source: PIE on GitHub
Where older books and tutorials are outdated
-
PEAR (Kromann ch. 7, Wiley) is obsolete for libraries; most books besides Kromann ignore Composer entirely.
-
Composer 1 (2012-2020) is end of life; Composer 2 is much faster, parallelises downloads and supports platform checks at runtime (
vendor/composer/platform_check.php). -
composer updateon servers: deploy withcomposer install --no-devfrom the committed lock file. -
composer auditandbump(2.4), blocking of insecure versions during updates (2.9) and the unifiedconfig.policysetting (2.10) are newer than every book. -
PECL for extensions is being replaced by PIE.