diff --git a/doc/articles/composer-platform-dependencies.md b/doc/articles/composer-platform-dependencies.md new file mode 100644 index 000000000..a3f2cadb2 --- /dev/null +++ b/doc/articles/composer-platform-dependencies.md @@ -0,0 +1,77 @@ + + +# Composer platform dependencies + +## What are platform dependencies + +Composer makes information about the environment Composer runs in available as virtual packages. This allows other +packages to define dependencies ([require](../04-schema.md#require), [conflict](../04-schema.md#conflict), +[provide](../04-schema.md#provide), [replace](../04-schema.md#replace)) on different aspects of the platform, like PHP, +extensions or system libraries, including version constraints. + +When you require one of the platform packages no code is installed. The version numbers of platform packages are +derived from the environment Composer is executed in and they cannot be updated or removed. They can however be +overwritten for the purposes of dependency resolution with a [platform configuration](../06-config.md#platform). + +**For example:** If you are executing `composer update` with a PHP interpreter in version +`7.4.42`, then Composer automatically adds a package to the pool of available packages +called `php` and assigns version `7.4.42` to it. + +That's how packages can add a dependency on the used PHP version: + +```json +{ + "require" : { + "php" : ">=7.4" + } +} +``` + +Composer will check this requirement against the currently used PHP version when running the composer command. + +### Different types of platform packages + +The following types of platform packages exist and can be depended on: + +1. PHP (`php` and the subtypes: `php-64bit`, `php-ipv6`, `php-zts` `php-debug`) +2. PHP Extensions (`ext-*`, e.g. `ext-mbstring`) +3. PHP Libraries (`lib-*`, e.g. `lib-curl`) +4. Composer (`composer`, `composer-plugin-api`, `composer-runtime-api`) + +To see the complete list of platform packages available in your environment +you can run `php composer.phar show --platform` (or `show -p` for short). + +The differences between the various Composer platform packages are explained further in this document. + +## Plugin package `composer-plugin-api` + +You can modify Composer's behavior with [plugin](plugins.md) packages. Composer provides a set of versioned APIs for +plugins. Because internal Composer changes may **not** change the plugin APIs, the API version may not increase every +time the Composer version increases. E.g. In Composer version `2.3.12`, the `composer-plugin-api` version could still +be `2.2.0`. + +## Runtime package `composer-runtime-api` + +When applications which were installed with Composer are run (either on CLI or through a web request), they require the +`vendor/autoload.php` file, typically as one of the first lines of executed code. Invocations of the Composer +autoloader are considered the application "runtime". + +Starting with version 2.0, Composer makes [additional features](../07-runtime.md) (besides registering the class autoloader) available to the application runtime environment. + +Similar to `composer-plugin-api`, not every Composer release adds new runtime features, +thus the version of `composer-runtimeapi` is also increased independently from Composer's version. + +## Composer package `composer` + +Starting with Composer 2.2.0, a new platform package called `composer` is available, which represents the exact +Composer version that is executed. Packages depending on this platform package can therefore depend on (or conflict +with) individual Composer versions to cover edge cases where neither the `composer-runtime-api` version nor the +`composer-plugin-api` was changed. + +Because this option was introduced with Composer 2.2.0, it is recommended to add a `composer-plugin-api` dependency on +at least `>=2.2.0` to provide a more meaningful error message for users running older Composer versions. + +In general, depending on `composer-plugin-api` or `composer-runtime-api` is always recommended +over depending on concrete Composer versions with the `composer` platform package. diff --git a/src/Composer/Repository/PlatformRepository.php b/src/Composer/Repository/PlatformRepository.php index ee2199804..00e97f46f 100644 --- a/src/Composer/Repository/PlatformRepository.php +++ b/src/Composer/Repository/PlatformRepository.php @@ -31,7 +31,7 @@ use Composer\XdebugHandler\XdebugHandler; */ class PlatformRepository extends ArrayRepository { - const PLATFORM_PACKAGE_REGEX = '{^(?:php(?:-64bit|-ipv6|-zts|-debug)?|hhvm|(?:ext|lib)-[a-z0-9](?:[_.-]?[a-z0-9]+)*|composer-(?:plugin|runtime)-api)$}iD'; + const PLATFORM_PACKAGE_REGEX = '{^(?:php(?:-64bit|-ipv6|-zts|-debug)?|hhvm|(?:ext|lib)-[a-z0-9](?:[_.-]?[a-z0-9]+)*|composer(?:-(?:plugin|runtime)-api)?)$}iD'; /** * @var ?string @@ -124,6 +124,12 @@ class PlatformRepository extends ArrayRepository } } + $prettyVersion = Composer::getVersion(); + $version = $this->versionParser->normalize($prettyVersion); + $composer = new CompletePackage('composer', $version, $prettyVersion); + $composer->setDescription('Composer package'); + $this->addPackage($composer); + $prettyVersion = PluginInterface::PLUGIN_API_VERSION; $version = $this->versionParser->normalize($prettyVersion); $composerPluginApi = new CompletePackage('composer-plugin-api', $version, $prettyVersion); diff --git a/tests/Composer/Test/Repository/PlatformRepositoryTest.php b/tests/Composer/Test/Repository/PlatformRepositoryTest.php index 27a9ddbbf..ca95ae556 100644 --- a/tests/Composer/Test/Repository/PlatformRepositoryTest.php +++ b/tests/Composer/Test/Repository/PlatformRepositoryTest.php @@ -12,6 +12,7 @@ namespace Composer\Test\Repository; +use Composer\Composer; use Composer\Package\Link; use Composer\Package\PackageInterface; use Composer\Repository\PlatformRepository; @@ -1213,6 +1214,64 @@ Linked Version => 1.2.11', self::assertTrue($link->getConstraint()->matches($this->getVersionConstraint('=', $sourcePackage->getVersion()))); } } + + public function testComposerPlatformVersion() + { + $runtime = $this->getMockBuilder('Composer\Platform\Runtime')->getMock(); + $runtime + ->method('getExtensions') + ->willReturn(array()); + $runtime + ->method('getConstant') + ->willReturnMap( + array( + array('PHP_VERSION', null, '7.0.0'), + array('PHP_DEBUG', null, false), + ) + ); + + $platformRepository = new PlatformRepository(array(), array(), $runtime); + + $package = $platformRepository->findPackage('composer', '='.Composer::getVersion()); + self::assertNotNull($package, 'Composer package exists'); + } + + public static function providePlatformPackages() + { + return array( + array('php', true), + array('php-debug', true), + array('php-ipv6', true), + array('php-64bit', true), + array('php-zts', true), + array('hhvm', true), + array('hhvm-foo', false), + array('ext-foo', true), + array('ext-123', true), + array('extfoo', false), + array('ext', false), + array('lib-foo', true), + array('lib-123', true), + array('libfoo', false), + array('lib', false), + array('composer', true), + array('composer-foo', false), + array('composer-plugin-api', true), + array('composer-plugin', false), + array('composer-runtime-api', true), + array('composer-runtime', false), + ); + } + + /** + * @param string $packageName + * @param bool $expectation + * @dataProvider providePlatformPackages + */ + public function testValidPlatformPackages($packageName, $expectation) + { + self::assertSame($expectation, PlatformRepository::isPlatformPackage($packageName)); + } } class ResourceBundleStub