Lock, Install and Audit PHP Dependencies with Composer

A missing composer.lock file is how "works on my machine" becomes a production incident, and Composer is what keeps it from happening. This covers keeping composer.json, composer.lock and vendor/ aligned while you add, install and audit PHP dependencies, and checking the platform and security state before you hand the project to another machine.

Allow about fifteen minutes for a small project, plus package download time. Examples use Composer 2.7.1, installed here as Debian package composer 2.7.1-2ubuntu0.1~esm1; exact diagnostics and dependency choices vary by release, repository metadata and PHP version. Run these as the project user: root-owned files here are a problem your application user cannot fix later.

1. Check the installed command and project

Start in the directory containing your project's composer.json. This is read-only:

$ command -v composer
/usr/bin/composer
$ composer --version
Composer version 2.7.1 2024-02-09 15:26:28
$ test -f composer.json && echo 'composer.json found'
composer.json found

If the file lives elsewhere, use the global --working-dir option instead of changing your shell's directory:

$ composer --working-dir=/path/to/project validate

Checkpoint: you have confirmed the binary and the exact directory you intend to change. Do not run a dependency command from a parent directory and hope Composer finds the right project.

2. Validate the dependency files

Run validate before installing or updating anything. It checks the project metadata and, when present, the lock file:

$ composer validate
./composer.json is valid
./composer.lock is valid

The exact lines depend on whether a lock file exists. A validation failure is a stop sign: fix the JSON or lock file and repeat the command. Warning: do not paper over a schema error by deleting composer.lock, that changes the resolution input and can pull in a different set of packages entirely.

3. Install the locked versions

For a checkout that already has composer.lock, use install: Composer uses the exact recorded versions and writes them under vendor/. This is the normal command for a deployment, a fresh checkout, or CI:

$ composer install --no-interaction --no-progress
Installing dependencies from lock file (including require-dev)
Verifying lock file contents can be installed on current platform.
Generating autoload files

Output varies by project; what matters is a zero exit status and a real autoloader:

$ printf 'install status: %s\n' "$?"
install status: 0
$ test -f vendor/autoload.php && echo 'autoload ready'
autoload ready

If the project defines plugins or scripts, installation may run them. For a cautious or restricted build, consider --no-plugins --no-scripts, then run the project's required setup explicitly after reviewing it: those flags can leave a project incomplete if its scripts generate required files.

4. Add a dependency deliberately

Use require when the project should start depending on a package. It edits composer.json, resolves dependencies, and normally updates composer.lock and vendor/. Replace the placeholder package and constraint with one you have actually reviewed:

$ composer require vendor/package:^1.2 --no-interaction
./composer.json has been updated
Loading composer repositories with package information
Updating dependencies
Writing lock file
Installing dependencies from lock file
Generating autoload files

Check the change before committing it:

$ git diff -- composer.json composer.lock
$ composer validate
$ composer audit

Warning: require is not a harmless lookup, it downloads code, may run package or project scripts, and can pull in transitive updates to satisfy the new constraint. If the result is wrong, restore the two tracked files from version control and remove the generated vendor changes through your normal recovery process; do not treat composer update as an undo command, it resolves versions again from scratch.

5. Update only when you mean to resolve again

install follows the lock file. update reads the constraints in composer.json, resolves a fresh permitted set of versions, and rewrites composer.lock, making it a deliberate change rather than a routine deployment step:

$ composer update vendor/package --with-dependencies --no-interaction
Loading composer repositories with package information
Updating dependencies
Writing lock file
Installing dependencies from lock file
Generating autoload files

Name a single package when only one direct dependency needs attention; without an argument, Composer considers the whole graph and may update far more than you expected. Review the lock-file diff, run the test suite, and repeat composer audit before committing.

There is no safe universal rollback. The reliable recovery is to restore the previous reviewed composer.json and composer.lock, then run composer install; keep the old lock file until the new setup has passed its checks.

6. Check platform and security boundaries

A lock file cannot make an unsuitable PHP runtime suitable. Check platform requirements after an install or when moving to another host:

$ composer check-platform-reqs
No vendor dir present. Please run "composer install" first.

On an installed project it reports each PHP extension and runtime requirement. A failure means the host needs a compatible runtime or extension; --ignore-platform-reqs is not a fix, it can turn an install-time warning into a production failure.

$ composer audit
No security vulnerability advisories found.

The result depends on current advisory data and your actual packages. Treat any advisory as a release decision that needs investigating, not something to suppress just to make a build go green.

7. Refresh autoloading without changing versions

Changed the autoload mapping, or added a class under an existing one? Regenerate the autoloader:

$ composer dump-autoload
Generating autoload files

This never chooses new dependency versions. If the mapping is wrong, fix the namespace and path in the project file and run it again. A reviewed --classmap-authoritative or --optimize can change autoload behaviour for production, so treat either as a deliberate choice, not performance folklore.

Common failure traps

Done means