composer-upgrade
Composer Upgrade
Upgrade Workflow
Follow this sequence when upgrading a PHP project:
- Identify what's outdated →
composer outdated - Diagnose blockers →
composer why-not vendor/package version - Trace dependencies →
composer why vendor/package - Update packages →
composer update vendor/package --with-all-dependencies - Test
- Harden constraints →
composer bump(applications only)
See references/commands.md for full flag reference. See references/upgrade-workflow.md for detailed strategies.
Core Commands
composer outdated
Lists packages with newer versions available.
composer outdated # all packages
composer outdated --direct # only packages in require/require-dev
composer outdated symfony/* # filter by pattern
composer outdated --format=json # machine-readable output
Reading the output:
- Red = semver major bump (breaking changes likely)
- Yellow = semver minor/patch (safe upgrade)
!marker = package is not semver-safe (minor/patch but breaking)
Columns: name | current | latest | description
composer why-not
Shows what prevents upgrading a package to a specific version.
composer why-not vendor/package 2.0
composer why-not php 8.2 # check what blocks a PHP version requirement
composer why-not vendor/package "*" # check what blocks any upgrade
Output shows the dependency chain: which packages require conflicting versions.
composer why
Shows which installed packages depend on a given package.
composer why vendor/package
composer why-not vendor/package # inverse: what conflicts with it
composer update
composer update # update all (risky on large projects)
composer update vendor/package # update one package
composer update vendor/package --with-all-dependencies # also update its deps
composer update --dry-run # preview changes without applying
Common Patterns
"Why can't I update X?"
composer why-not vendor/package 3.0
Read the output to find which package constrains it, then check if that constraining package itself can be updated.
"What's blocking my PHP version upgrade?"
composer why-not php 8.2
Lists every package that lacks a php: ^8.2 constraint, sorted by most blocking.
"Safe incremental upgrade"
Prefer updating direct dependencies one at a time with --dry-run first:
composer update vendor/package --with-all-dependencies --dry-run
composer update vendor/package --with-all-dependencies
Relaxing constraints in composer.json
When why-not reveals a constraint in your own composer.json, update the version constraint and re-run:
"require": {
"vendor/package": "^3.0" // was "^2.0"
}
Then: composer update vendor/package
Hardening constraints after upgrading (applications)
After updating packages in an application, run composer bump to raise the lower bounds of constraints in composer.json to the currently installed versions:
composer bump # harden all constraints
composer bump vendor/package # harden one package
composer bump --dev-only # only require-dev (safe for libraries too)
Before: "symfony/console": "^6.0" → After: "symfony/console": "^6.4.3"
This prevents future composer install runs from resolving older versions that weren't tested. It does not prevent future minor/patch upgrades — the ^ is preserved.
Enable auto-bump in composer.json for applications:
{
"config": {
"bump-after-update": true
}
}
With this set, Composer automatically runs bump after every composer update. Use "dev" or "no-dev" to limit which dependency group is bumped.
Applications only: Do not run
composer bump(without--dev-only) on libraries. Narrowing lower bounds of library dependencies causes version conflicts for downstream consumers.