Composer 2: What's new and changed

Published On2020-04-08

What's new and changing in Composer version 2

Composer, PHP dependency manager was released about 8 years ago, and its second major version is just around the corner. Over the years, Composer received many new features, and kept up with PHP standards. Composer version 2 will be mostly compatible with your existing workflows, while bringing some more great new features.

Faster download times

One of the most noticeable changes would be its performance. Packages and meta data are downloaded parallel, which can significantly improve the time it takes to download packages.

Here is a comparison between composer v1.10.5 and v2 (40a35ab) to run composer require laravel/laravel. The test was done on an empty cache, followed by a new test after the cache is primed. The test results are the average of 5 runs on consumer hardware.

A chart showing installation for larave/laravel package on Composer v1 and v2

Composer v2 was nearly 2 times faster to install laravel/laravel without caching.

This performance boost comes from parallel downloading of package meta data (which also new endpoints) and package zip files.

When curl is present, multiple packages/API calls will be downloaded all at the same time, reducing the overall download time. Furthermore, Composer v2 will make use of HTTP/2, and share TLS sessions, and DNS responses between HTTP requests to speed up downloads.

For Composer v1, hirak/prestissimo plugin brought these features for quite some time.

Partial offline support

You can prevent Composer v2 from attempting to make any network requests. This can come handy if you want to run benchmarks, or if your Internet connection is faulty. Composer v2 will try to install the packages provided a composer.lock file exists and all packages and meta data are cached.

For this to work, set an environment variable COMPOSER_DISABLE_NETWORK, with value 1. Project composer.lock file must be present for it to work.

Note that this will completely disable Composer from making network requests. It will not work as a fallback mechanism.

With the environment variable set, you can use composer install command as you would do otherwise. Composer will show a warning that network is disabled, but it will continue anyway.

https://repo.packagist.org could not be fully loaded (Network disabled, request canceled: https://repo.packagist.org/packages.json), package information was loaded from the local cache and may be out of date

If a package is not available in cache, you will get an error:

The required git reference for ayesh/php-timer is not in cache and network is disabled, aborting

Run-time platform requirements check

Composer v2 will create an additional platform_check.php file that checks the platform requirements (PHP version and loaded extensions) when the autoloader is loaded.

This is covered in length at Composer 2: Platform Check.

Dry-run support for require and remove commands

composer update command has a --dry-run option, that prevents composer from actually making any changes, but simply display the output in terminal.

Composer v2 brings --dry-run option to composer require and composer remove commands too, which you can use to test package installation/removal without impacting actual project files.

Running as root requires confirmation

Composer plugins and scripts can run arbitrary commands on the system. Running Composer as the root user is often a bad idea because a malicious plugin/script can execute commands as root too.

Prior to version 2, Composer raised a warning message when you try to run a command as root:

Do not run Composer as root/super user! See https://getcomposer.org/root for details

With Composer version 2, you will get an interactive confirmation:

Do not run Composer as root/super user! See https://getcomposer.org/root for details
Continue as root/super user [yes]?

This confirmation will not appear if it is not supported on your terminal is not interactive. You can force this mode by passing the -n / --no-interaction flag to the command. E.g.:

composer install --no-interaction

New repository metadata format

Composer v2 supports a new format for its repository meta data. Traditionally, Composer metadata files were large sharded JSON files, which required a few megabytes of downloads. The new format is light-weight, and repository maintainers provide URL endpoints for individual packages, which Composer can request and cache.

Packagist.org already supports this format. If you are using other repositories, look for metadata-url key in its packages.json file.

Packagist returns this:

{
    "packages": [],
    "metadata-url": "/p2/%package%.json",
    "provider-includes": {...}
    ...
}

When the metadata-url is present, Composer v2 will use the new meta format endpoint. Composer v1 will continue to use the standard approach.

Canonical, filtering, and allow-only support for multiple repositories

Packagist.org is the default repository for Composer. But that doesn't mean you cannot use other repositories in your project.

Drupal has its official Composer repository, and WordPress has an unofficial repository, and your team/organization probably has one too.

When Composer looks up for a package, it queries all configured repositories, and finally, the default repository, which is packagist.org (unless configured otherwise). With Composer version 2, you can further control how Composer should work with multiple repositories in a single project.

Repository Priorities

With Composer v2, Composer will look for packages in all repositories configured in the composer.json files repositories property in the order they are set. If a package is found in a repository, Composer will not look for that package in any other repositories further down the line.

Canonical Repositories

Composer v2 will consider all repositories to be canonical, and when a package is found in a repository, repositories with lower priorities cannot provide the same package, even if a newer version is available.

Canonical repositories help prevent accidentally installing packages from lower priority repositories (such as packagist.org) when the package is found in private or package-specific repositories. Without canonical repositories, Composer might install a package from a lower priority repository if a newer version is found.

You can override this behavior by marking the repository as not canonical:

{
    ...
    "repositories": [
        {
            "type": "composer",
            "url": "https://example.org",
            "canonical": false
        }
    ]
    ...
}

Filtered Repositories

Composer v2 supports only and exclude directives in repository configuration. They tell Composer to only look for packages with an exact match or a pattern match in said repositories.

{
    "repositories":[
        {
            "type":"composer",
            "url":"https://packages.drupal.org/8",
            "only": ["drupal/*"]
        },
        {
            "type":"composer",
            "url":"https://wpackagist.org",
            "only": ["wpackagist-plugin/*", "wpackagist-theme/*"]
        }
    ],
}

Composer will look for packages that match drupal/* in the Drupal repo, and wpackagist-plugin/* and wpackagist-theme/* in WPackagist repos. If these repositories host packages for other vendor names, they will not be lookup up, nor used.

If these repositories do not host a package that match the only repository, they will still be lookup up on other repositories further down the line.

You can specify specific package names in the only directive as well.

You can also use the exclude directive within a repository configuration to exclude specific packages or a pattern from being used from a particular repository.

{
    "repositories":[
        {
            "type":"composer",
            "url":"https://example.com",
            "exclude": ["example/outdated-package"]
        }
    ],
}

With settings above, all packages will be tried from the example.com repo, except for example/outdated-package. One use case would be a repository down the line hosting a newer version of the same package that you want to use instead of the one available at example.com.

pear repository is removed

About a decade ago, PHP had PEAR, or PHP Extension and Application Repository to install reusable packages. Composer naturally took over with its nicer user experience and vastly openness and ease of use. Composer v1 had support to install PEAR packages from PEAR channels by adding a custom repo with type pear.

You can still install any PEAR packages that were hosted on php.net by simply installing using the pear/ vendor prefix. Support for custom PEAR repositories is removed in Composer v2.

Your existing PEAR packages with pear/ packages will likely continue to work, if they have moved to the pear/ vendor namespaces (which is the case for almost every maintained package).

--no-suggest option is removed deprecated

Composer v2 deprecates the --no-suggest option that was available in require and update commands. Suggested packages are now always shown, but their description is limited to one line to prevent excessively long terminal output.

You will get a notice saying this option is deprecated:

You are using the deprecated option "--no-suggest". It has no effect and will break in Composer 3.

A quick search at GitHub shows more than 400K repositories with YAML files (often used in CI configuration) with --no-suggest option, which will throw the error above with Composer v2 v3.

This option was once removed, but then updated to emit a deprecation notice because several packages and their CI/CD workflows would have failed otherwise.

Lock, download, and install workflow improvements

Composer v2 has an improved workflow for package installation and updates.

During an installation or an update, all packages are locked first (updated in composer.lock), then downloaded to cache (in parallel if possible). After all files are successfully downloaded, or when found in cache, Composer extracts them to the vendor-dir. This prevents broken/incomplete state in the vendor-dir in case the network fails in the middle of the process.

Furthermore, the vendor/composer/installed.json file is refactored to use packages property store all package information (as opposed to the root level in v1). For each package, its installation path is now stored in an install-path relative to the installed.json file. This file also stores whether the require-dev packages were installed. Plugin maintains can make use of this information enhance their plugins.

composer-plugin-api is now 2.0

This probably wouldn't surprise anyone. The plugin API provided by Composer v2 is marked 2.0. This will prevent plugins from being installed if they require composer-plugin-api with version constraint ^1.0.

If you maintain a Composer plugin, you will need to update this dependency to allow composer-plugin-api versions ^2.0. It may be possible to support both versions from the same version, as long as all new interface methods are implemented.

Most notably, plugins must now implement PluginInterface::deactivate() and PluginInterface::uninstall() methods. You can discuss and take a look at other plugins implementations at this issue.

This section in Composer documentation has more information and a summary of changes.

Try Composer v2 today!

You can try out Composer version 2 simply with composer self-update --snapshot command. If you have plugins that do not work, they can be temporarily disabled with composer --no-plugins.

If you find any bugs or have suggestions, create an issue or a pull-request.


Finally, let's take moment to appreciate Jordi, Nils, and other contributors immense efforts in bringing Composer to PHP. Composer changed PHP as we knew it last decade, made a lot of things we do with PHP today possible. Thank you!

Recent Articles on PHP.Watch

All ArticlesFeed
Attributes in PHP 8

Attributes in PHP 8

A comprehensive guide to PHP 8's new Attributes
PHP Ternary and php ternary Coalescing Operators

PHP Ternary and php ternary Coalescing Operators

A quick guide on PHP's Ternary Operators and Coalescing operators
Liskov Substitution Principle in PHP

Liskov Substitution Principle in PHP

How Liskov Substitution Principle applies to PHP, and PHPs contravariance and covariance.
Subscribe to PHP.Watch newsletter for monthly updates

You will receive an email on last Saturday of every month and on major PHP releases with new articles related to PHP, upcoming changes, new features and what's changing in the language. No marketing emails, no selling of your contacts, no click-tracking, and one-click instant unsubscribe from any email you receive.