PHP Composer

Composer is the standard PHP dependency manager. Install third-party packages, manage versions, autoload your own code, and ship reproducible builds with composer.lock.

Intermediate 9 min read 10 examples

What is Composer?

Composer is the de-facto package manager for PHP. It does three things:

  • Resolves & installs dependencies from Packagist (the public registry)
  • Generates an autoloader so you can use App\Service\X; without manual require
  • Locks exact versions in composer.lock for reproducible installs across machines

Install Composer

Bash
# macOS / Linux
curl -sS https://getcomposer.org/installer | php
sudo mv composer.phar /usr/local/bin/composer

# Windows - use the installer at getcomposer.org/download
# Or via Scoop: scoop install composer

# Verify
composer --version

composer.json Anatomy

JSONcomposer.json
{
    "name": "rubansoftwares/myapp",
    "description": "A short description of the project",
    "type": "project",
    "license": "MIT",
    "require": {
        "php": "^8.2",
        "guzzlehttp/guzzle": "^7.8",
        "vlucas/phpdotenv": "^5.6"
    },
    "require-dev": {
        "phpunit/phpunit": "^10.5",
        "phpstan/phpstan": "^1.10"
    },
    "autoload": {
        "psr-4": {
            "App\\": "src/"
        }
    },
    "autoload-dev": {
        "psr-4": {
            "App\\Tests\\": "tests/"
        }
    },
    "scripts": {
        "test":    "phpunit",
        "analyse": "phpstan analyse src --level=7"
    },
    "config": {
        "sort-packages": true,
        "optimize-autoloader": true
    }
}

Installing Packages

Bash
# Initialise a new project
composer init

# Install a runtime dependency
composer require guzzlehttp/guzzle

# Install a dev-only dependency
composer require --dev phpunit/phpunit

# Install everything from composer.json
composer install

# Install in production (skip require-dev, optimize autoloader)
composer install --no-dev --optimize-autoloader

# Remove a package
composer remove vlucas/phpdotenv

Version Constraints

ConstraintMatches
1.2.3Exactly 1.2.3
^1.2.3>=1.2.3 <2.0.0 (most common)
~1.2.3>=1.2.3 <1.3.0
~1.2>=1.2.0 <2.0.0
1.2.*>=1.2.0 <1.3.0
>=1.2 <2.0Inclusive range
1.0 || 2.01.0.x OR 2.0.x
dev-mainLatest commit on main branch
Use caret (^) by default

Caret follows SemVer - "allow anything that should be backwards compatible". For packages that pre-1.0 (^0.5.2) caret tightens to ~0.5.2 automatically since 0.x is treated as unstable.

composer.lock

composer.lock records the exact version, commit hash, and dependency tree resolved at install time. Always commit it.

Bash
# composer.lock exists -> install exact versions
composer install

# Update one package and refresh its lock entry
composer update guzzlehttp/guzzle

# Update everything to latest allowed by composer.json
composer update

# See what would update (no changes)
composer outdated --direct

Autoloading Your Code

PHPpublic/index.php
<?php
require __DIR__ . "/../vendor/autoload.php";

use App\Service\UserService;
use GuzzleHttp\Client;

$users = new UserService();
$http  = new Client();

After editing autoload sections in composer.json, regenerate:

Bash
composer dump-autoload                      # standard
composer dump-autoload --optimize           # production (build classmap)
composer dump-autoload --classmap-authoritative  # strictest, fastest

Composer Scripts

Define short aliases for common commands. Run with composer run-script test or just composer test:

JSON
"scripts": {
    "test":     "phpunit",
    "analyse":  "phpstan analyse src --level=7",
    "fix":      "php-cs-fixer fix",
    "check":    ["@analyse", "@test"],
    "post-install-cmd": ["@php artisan key:generate"]
}

Private Packages & Patches

JSON
{
    "repositories": [
        {
            "type": "vcs",
            "url": "git@github.com:my-org/private-lib.git"
        },
        {
            "type": "path",
            "url": "../sibling-package"
        }
    ],
    "require": {
        "my-org/private-lib": "^2.1"
    }
}

Essential Commands Cheatsheet

CommandUse
composer initCreate composer.json interactively
composer require pkgAdd and install a package
composer require --dev pkgAdd as a dev dependency
composer installInstall from lock file
composer updateUpdate to latest allowed
composer outdatedShow outdated packages
composer auditCheck for known security vulns
composer why pkgShow why a package is installed
composer show pkgInspect package details
composer dump-autoload -oRebuild optimised autoloader

Next Steps

Frequently Asked Questions

Yes - always. It pins exact versions so every developer and every deploy installs the same code. Without it, composer install picks whatever satisfies your constraints, which can drift across machines.

composer install reads composer.lock and installs the exact pinned versions. composer update ignores the lock, resolves the latest versions satisfying composer.json, and rewrites the lock. Use install on deploy; use update intentionally to bump versions.

For packages only needed during development: PHPUnit, PHPStan, Psalm, code formatters. They won't install in production when you run composer install --no-dev, keeping the vendor folder smaller and the attack surface narrower.

Caret means "compatible with 7.4" - allow any 7.x.y where x.y >= 4.0. Semantic versioning promises no breaking changes within a major version. ~7.4 is stricter (only 7.4.x). >=7.4 is looser (any newer including major bumps).

composer update vendor/package --with-dependencies. The lock file changes only for that package and its direct deps.