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.
The route
Jump straight to the step you need, or tick off Done means at the end.
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
--no-interactionstops prompts in automation.--no-progresskeeps terminal control characters out of logs.- Neither option changes which versions get installed.
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
- Lock drift. Editing
composer.jsonby hand can makeinstallwarn the lock file is out of date; review the intended constraint and run a plannedupdaterather than ignoring the warning. - Wrong directory. Use
--working-dir, or check withpwdandls composer.json, before changing dependencies. - Hidden execution. Plugins and scripts are code: review trusted project configuration first, and use
--no-pluginsor--no-scriptswhere your build policy requires it. - Permission damage. Do not alternate between your user and
sudo composer; repair ownership through your normal admin process if it has already happened. - Ambiguous success. Composer documents exit status 0 as success, 1 as a generic error, 2 as a dependency-solving error. Check the status in automation rather than parsing coloured text.
Done means
- Command and project confirmed.
composer --versionand the working directory match what you intend. - Validation passes.
composer validatesucceeds before any dependency change is committed. - Deployments use install. The reviewed lock file drives
composer install, not an implicit update. - Updates are narrow and reviewed. Diffs were checked and tests run before committing.
- Platform and audit checked.
composer check-platform-reqsandcomposer auditpass, or exceptions are documented. - Scripts considered. Plugins and scripts were reviewed before any command that can run them.