Hardly any PHP application works without third-party libraries, for example for sending email, generating PDFs or entire frameworks such as Laravel. Composer, often called the package manager for PHP, takes care of including these packages. Unlike operating system package managers, Composer works per project: the packages end up in the vendor folder of the respective project. The default source is the public Packagist repository. You can also add your own or private package sources.
How Composer works
- composer.json: This file states which packages a project needs and in which versions. Version constraints such as
^3.2allow compatible updates. Many packages follow Semantic Versioning for this, where a new major version signals incompatible changes. - composer.lock: After resolution, Composer records the exact installed versions of all packages, including indirect dependencies. According to the documentation, this file should be committed to the repository so that everyone involved, the CI/CD pipeline and the production server use the same versions.
- vendor: The folder with the third-party code, by contrast, does not belong in version control. It is recreated with every installation.
- Autoloading: Composer generates a file that PHP uses to find the packages' classes automatically.
The difference between the central commands is important: composer install installs exactly the versions from the lock file. composer update resolves the dependencies again, fetches newer matching versions and rewrites the lock file.
Key commands
composer require anbieter/paketadds a package.composer install --no-devinstalls for production without development tools.composer update anbieter/paketupdates a single package in a targeted way.composer outdatedshows which packages have newer versions.composer auditchecks the installed packages for known vulnerabilities.
Security in the software supply chain
Every package is third-party code that runs with the application's permissions. Composer offers several aids for this:
- composer audit: According to the documentation, the command checks the packages against security advisories via the Packagist API by default and also reports abandoned packages. If it finds issues, it exits with an error code, which makes it suitable as an automated check in a pipeline. With
--locked, it checks the lock file instead of the vendor folder. - allow-plugins: Composer plugins can execute code during a Composer run. Since Composer 2.2, the
allow-pluginssetting defines which plugins may do so. Without an entry, no plugin is loaded. - Review the lock file: Look at changes to composer.lock during code review. New packages and unexpected sources stand out this way.
In addition:
- Evaluate new packages before adding them: Who maintains them, how active is the project, and how many further dependencies does it bring along?
- Watch out for typos in package names, because similar-sounding names are a known attack vector.
- Do not store credentials for private package sources in the repository.
- Update regularly instead of letting updates pile up. Small steps are easier to test.
- Track discovered vulnerabilities in vulnerability management.
Typical use cases
- An online shop includes payment and shipping modules as packages and updates them in a targeted way.
- A pipeline runs
composer auditon every pull request and stops on known vulnerabilities. - Before a PHP upgrade, Composer shows which packages do not yet support the new version, see PHP end of life.
How it differs
Composer manages PHP libraries at the project level. PHP extensions written in C that extend the interpreter itself, by contrast, are installed via the operating system or PECL. For JavaScript, npm fulfills a similar role to Composer. Git versions your own code, Composer the third-party building blocks.
Frequently asked questions
How often should dependencies be updated?
Security updates as soon as possible, once composer audit or a security advisory reports them. For all other updates, a fixed rhythm has proven useful, for example monthly, so that changes do not pile up over years into one large package that is hard to test.
What should you do if composer audit reports a vulnerability?
First read the advisory: Which versions are affected, and does your own application use the affected function? Then update the package in a targeted way, test it and deploy it. If no update is available, document the vulnerability with a justification and a compensating measure, and check again regularly.
May Composer run on the production server?
The usual approach is to install dependencies in the pipeline and deploy the finished package. If Composer does run on the server, use composer install --no-dev based on the reviewed lock file and never composer update.