We used to download a library file, drop it in a folder and call it with require. The result was forgotten old versions that nobody could locate. Composer brings order to this: you say which packages you need and which versions are acceptable, and it does the rest.

Laravel is built entirely on Composer, and even in WordPress most professional tools are installed with it. So learning it is one of the first investments for a PHP developer.

What Composer actually does#

Composer is PHP's package manager. It has three main jobs:

  1. It downloads the packages you need, and their dependencies, from the public Packagist registry.
  2. It finds compatible versions and records them exactly.
  3. It builds an autoloader so classes load without a manual require.

Install it by following the official guide at getcomposer.org, because the method differs by operating system. After installing, composer --version should respond.

Starting a project and installing a package#

Run this in the project folder:

Bash
composer init
composer require monolog/monolog

The first command creates the composer.json file and the second installs the package. Three things now appear: composer.json, composer.lock and the vendor folder. Simple usage:

PHP
<?php
declare(strict_types=1);

require __DIR__ . '/vendor/autoload.php';

use Monolog\Logger;
use Monolog\Handler\StreamHandler;

$log = new Logger('app');
$log->pushHandler(new StreamHandler(__DIR__ . '/app.log'));
$log->warning('User signed in');

A single require for the autoloader is enough; all other classes are found automatically.

composer.json and composer.lock#

You write composer.json, and it says what you want:

JSON
{
    "name": "acme/shop",
    "require": {
        "php": "^8.1",
        "monolog/monolog": "^3.0"
    },
    "require-dev": {
        "phpunit/phpunit": "^10.0"
    },
    "autoload": {
        "psr-4": {
            "App\\": "src/"
        }
    }
}

The ^3.0 constraint means any version compatible with 3 that has no breaking changes. Composer itself writes composer.lock, recording the exact version of every package. The golden rule is:

FileWho writes itIn Git?
composer.jsonYouYes
composer.lockComposerYes (for applications)
vendor/ComposerUsually no

The lock file guarantees that your server and your machine run exactly the same versions. To manage these files in Git, see Git for beginners.

Autoloading with PSR-4#

The autoload section in the example above says that classes in the App namespace live in the src folder. So the class App\Service\Mailer must be in src/Service/Mailer.php:

PHP
<?php
declare(strict_types=1);

namespace App\Service;

final class Mailer
{
    public function send(string $to, string $subject): bool
    {
        // sending logic
        return true;
    }
}

After changing the autoload section, run composer dump-autoload to rebuild the map. The file name must match the class name, with the same upper and lower case, because Linux servers are case-sensitive.

Everyday commands#

  • composer install: installs the versions recorded in the lock file. Use it on servers and for teammates.
  • composer update: updates packages within the constraints and changes the lock file. Use it on your development machine.
  • composer require vendor/pkg: adds a new package.
  • composer remove vendor/pkg: removes a package.
  • composer outdated: lists packages that have newer versions.
  • composer audit: checks your dependencies against a list of known security vulnerabilities.

Run composer audit every so often; an outdated package is one of the most common ways in for an attacker. We cover the other ways in PHP code security.

Deploying on shared hosting#

On a production server, don't install development packages, and optimize the autoloader:

Bash
composer install --no-dev --optimize-autoloader

If your host has no terminal access, run the same command on your own machine and upload the vendor folder. Keep your local PHP version close to the host's version. Other shared hosting tips are in the shared hosting checklist for Laravel.

Never leave the vendor folder directly accessible from the web. It is better placed outside the site's public root.

Safety notes#

Don't install unknown packages: check the repository name, the number of users and the date of the last update. Installing a package means running code you didn't write. If you run Composer as root, those same scripts run with full privileges. Also, don't put passwords or API keys in composer.json.

When something breaks#

You will see three problems more than any others. First, the "Class not found" message, which almost always means the namespace doesn't match the file path, or composer dump-autoload wasn't run after changing autoload. Second, version conflicts: two packages want incompatible versions of one dependency. The command composer why-not vendor/pkg 2.0 explains what is blocking the install. Third, PHP version incompatibility; the config.platform.php section in composer.json tells Composer to resolve versions for the host's PHP, not your machine's PHP.

JSON
{
    "config": {
        "platform": { "php": "8.1.0" },
        "sort-packages": true
    }
}

How to spot a good package#

Before composer require, spend two minutes. Check whether the repository is still maintained, how many open issues are going unanswered, how many other packages depend on it and whether its license fits your project. A package that brings a hundred dependencies for a ten-line job is usually not worth it. Every new dependency is code you must keep updated and a risk you must accept.

Summary#

  • Composer manages packages, versions and autoloading.
  • composer.json is what you want and composer.lock is the exact installed version; keep both in Git.
  • Run composer install on the server and run update only on your development machine.
  • PSR-4 autoloading means the file name, folder and namespace must all agree.
  • In production, use --no-dev --optimize-autoloader.
  • Run composer audit regularly and install only trusted packages.