Add a Composer\Versions class which is available in all projects at runtime to query installed packages/versions
parent
c757c9fa37
commit
0d1922dc27
@ -0,0 +1,186 @@
|
||||
<?php
|
||||
|
||||
namespace Composer;
|
||||
|
||||
use Composer\Semver\VersionParser;
|
||||
|
||||
/**
|
||||
* This class is copied in every Composer installed project and available to all
|
||||
*
|
||||
* To require it's presence, you can require `composer-runtime-api ^2.0`
|
||||
*/
|
||||
class InstalledVersions
|
||||
{
|
||||
private static $installed;
|
||||
|
||||
/**
|
||||
* Returns a list of all package names which are present, either by being installed, replaced or provided
|
||||
*
|
||||
* @return string[]
|
||||
* @psalm-return list<string>
|
||||
*/
|
||||
public static function getInstalledPackages()
|
||||
{
|
||||
return array_keys(self::$installed['versions']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether the given package is installed
|
||||
*
|
||||
* This also returns true if the package name is provided or replaced by another package
|
||||
*
|
||||
* @param string $packageName
|
||||
* @return bool
|
||||
*/
|
||||
public static function isInstalled($packageName)
|
||||
{
|
||||
return isset(self::$installed['versions'][$packageName]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether the given package satisfies a version constraint
|
||||
*
|
||||
* e.g. If you want to know whether version 2.3+ of package foo/bar is installed, you would call:
|
||||
*
|
||||
* Composer\InstalledVersions::satisfies(new VersionParser, 'foo/bar', '^2.3')
|
||||
*
|
||||
* @param VersionParser $parser Install composer/semver to have access to this class and functionality
|
||||
* @param string $packageName
|
||||
* @param ?string $constraint A version constraint to check for, if you pass one you have to make sure composer/semver is required by your package
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function satisfies(VersionParser $parser, $packageName, $constraint)
|
||||
{
|
||||
$constraint = $parser->parseConstraints($constraint);
|
||||
$provided = $parser->parseConstraints(self::getVersionRanges($packageName));
|
||||
|
||||
return $provided->matches($constraint);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a version constraint representing all the range(s) which are installed for a given package
|
||||
*
|
||||
* It is easier to use this via isInstalled() with the $constraint argument if you need to check
|
||||
* whether a given version of a package is installed, and not just whether it exists
|
||||
*
|
||||
* @param string $packageName
|
||||
* @return string Version constraint usable with composer/semver
|
||||
*/
|
||||
public static function getVersionRanges($packageName)
|
||||
{
|
||||
if (!isset(self::$installed['versions'][$packageName])) {
|
||||
throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
|
||||
}
|
||||
|
||||
$ranges = array();
|
||||
if (isset(self::$installed['versions'][$packageName]['pretty_version'])) {
|
||||
$ranges[] = self::$installed['versions'][$packageName]['pretty_version'];
|
||||
}
|
||||
if (array_key_exists('aliases', self::$installed['versions'][$packageName])) {
|
||||
$ranges = array_merge($ranges, self::$installed['versions'][$packageName]['aliases']);
|
||||
}
|
||||
if (array_key_exists('replaced', self::$installed['versions'][$packageName])) {
|
||||
$ranges = array_merge($ranges, self::$installed['versions'][$packageName]['replaced']);
|
||||
}
|
||||
if (array_key_exists('provided', self::$installed['versions'][$packageName])) {
|
||||
$ranges = array_merge($ranges, self::$installed['versions'][$packageName]['provided']);
|
||||
}
|
||||
|
||||
return implode(' || ', $ranges);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $packageName
|
||||
* @return string|null If the package is being replaced or provided but is not really installed, null will be returned as version, use satisfies or getVersionRanges if you need to know if a given version is present
|
||||
*/
|
||||
public static function getVersion($packageName)
|
||||
{
|
||||
if (!isset(self::$installed['versions'][$packageName])) {
|
||||
throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
|
||||
}
|
||||
|
||||
if (!isset(self::$installed['versions'][$packageName]['version'])) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return self::$installed['versions'][$packageName]['version'];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $packageName
|
||||
* @return string|null If the package is being replaced or provided but is not really installed, null will be returned as version, use satisfies or getVersionRanges if you need to know if a given version is present
|
||||
*/
|
||||
public static function getPrettyVersion($packageName)
|
||||
{
|
||||
if (!isset(self::$installed['versions'][$packageName])) {
|
||||
throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
|
||||
}
|
||||
|
||||
if (!isset(self::$installed['versions'][$packageName]['pretty_version'])) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return self::$installed['versions'][$packageName]['pretty_version'];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $packageName
|
||||
* @return string|null If the package is being replaced or provided but is not really installed, null will be returned as reference
|
||||
*/
|
||||
public static function getReference($packageName)
|
||||
{
|
||||
if (!isset(self::$installed['versions'][$packageName])) {
|
||||
throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
|
||||
}
|
||||
|
||||
if (!isset(self::$installed['versions'][$packageName]['reference'])) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return self::$installed['versions'][$packageName]['reference'];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
* @psalm-return array{name: string, version: string, reference: string, pretty_version: string, aliases: string[]}
|
||||
*/
|
||||
public static function getRootPackage()
|
||||
{
|
||||
return self::$installed['root'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the raw installed.php data for custom implementations
|
||||
*
|
||||
* @return array[]
|
||||
* @psalm-return array{root: array{name: string, version: string, reference: string, pretty_version: string, aliases: string[]}, versions: list<string, array{pretty_version: ?string, version: ?string, aliases: ?string[], reference: ?string, replaced: ?string[], provided: ?string[]}>}
|
||||
*/
|
||||
public static function getRawData()
|
||||
{
|
||||
return self::$installed;
|
||||
}
|
||||
|
||||
/**
|
||||
* Lets you reload the static array from another file
|
||||
*
|
||||
* This is only useful for complex integrations in which a project needs to use
|
||||
* this class but then also needs to execute another project's autoloader in process,
|
||||
* and wants to ensure both projects have access to their version of installed.php.
|
||||
*
|
||||
* A typical case would be PHPUnit, where it would need to make sure it reads all
|
||||
* the data it needs from this class, then call reload() with
|
||||
* `require $CWD/vendor/composer/installed.php` (or similar) as input to make sure
|
||||
* the project in which it runs can then also use this class safely, without
|
||||
* interference between PHPUnit's dependencies and the project's dependencies.
|
||||
*
|
||||
* @param array[] $data A vendor/composer/installed.php data set
|
||||
* @return void
|
||||
*
|
||||
* @psalm-param array{root: array{name: string, version: string, reference: string, pretty_version: string, aliases: string[]}, versions: list<string, array{pretty_version: ?string, version: ?string, aliases: ?string[], reference: ?string, replaced: ?string[], provided: ?string[]}>} $data
|
||||
*/
|
||||
public static function reload($data)
|
||||
{
|
||||
self::$installed = $data;
|
||||
}
|
||||
}
|
@ -0,0 +1,211 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Composer.
|
||||
*
|
||||
* (c) Nils Adermann <naderman@naderman.de>
|
||||
* Jordi Boggiano <j.boggiano@seld.be>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Composer\Test;
|
||||
|
||||
use Composer\Test\TestCase;
|
||||
use Composer\InstalledVersions;
|
||||
use Composer\Semver\VersionParser;
|
||||
|
||||
class InstalledVersionsTest extends TestCase
|
||||
{
|
||||
public function setUp()
|
||||
{
|
||||
InstalledVersions::reload(require __DIR__.'/Repository/Fixtures/installed.php');
|
||||
}
|
||||
|
||||
public function testGetInstalledPackages()
|
||||
{
|
||||
$names = array(
|
||||
'__root__',
|
||||
'a/provider',
|
||||
'a/provider2',
|
||||
'b/replacer',
|
||||
'c/c',
|
||||
'foo/impl',
|
||||
'foo/impl2',
|
||||
'foo/replaced',
|
||||
);
|
||||
$this->assertSame($names, InstalledVersions::getInstalledPackages());
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider isInstalledProvider
|
||||
*/
|
||||
public function testIsInstalled($expected, $name, $constraint = null)
|
||||
{
|
||||
$this->assertSame($expected, InstalledVersions::isInstalled($name));
|
||||
}
|
||||
|
||||
public static function isInstalledProvider()
|
||||
{
|
||||
return array(
|
||||
array(true, 'foo/impl'),
|
||||
array(true, 'foo/replaced'),
|
||||
array(true, 'c/c'),
|
||||
array(true, '__root__'),
|
||||
array(true, 'b/replacer'),
|
||||
array(false, 'not/there'),
|
||||
array(false, 'not/there', '^1.0'),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider satisfiesProvider
|
||||
*/
|
||||
public function testSatisfies($expected, $name, $constraint)
|
||||
{
|
||||
$this->assertSame($expected, InstalledVersions::satisfies(new VersionParser, $name, $constraint));
|
||||
}
|
||||
|
||||
public static function satisfiesProvider()
|
||||
{
|
||||
return array(
|
||||
array(true, 'foo/impl', '1.5'),
|
||||
array(true, 'foo/impl', '1.2'),
|
||||
array(true, 'foo/impl', '^1.0'),
|
||||
array(true, 'foo/impl', '^3 || ^2'),
|
||||
array(false, 'foo/impl', '^3'),
|
||||
|
||||
array(true, 'foo/replaced', '3.5'),
|
||||
array(true, 'foo/replaced', '^3.2'),
|
||||
array(false, 'foo/replaced', '4.0'),
|
||||
|
||||
array(true, 'c/c', '3.0.0'),
|
||||
array(true, 'c/c', '^3'),
|
||||
array(false, 'c/c', '^3.1'),
|
||||
|
||||
array(true, '__root__', 'dev-master'),
|
||||
array(true, '__root__', '^1.10'),
|
||||
array(false, '__root__', '^2'),
|
||||
|
||||
array(true, 'b/replacer', '^2.1'),
|
||||
array(false, 'b/replacer', '^2.3'),
|
||||
|
||||
array(true, 'a/provider2', '^1.2'),
|
||||
array(true, 'a/provider2', '^1.4'),
|
||||
array(false, 'a/provider2', '^1.5'),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider getVersionRangesProvider
|
||||
*/
|
||||
public function testGetVersionRanges($expected, $name)
|
||||
{
|
||||
$this->assertSame($expected, InstalledVersions::getVersionRanges($name));
|
||||
}
|
||||
|
||||
public static function getVersionRangesProvider()
|
||||
{
|
||||
return array(
|
||||
array('dev-master || 1.10.x-dev', '__root__'),
|
||||
array('^1.1 || 1.2 || 1.4 || 2.0', 'foo/impl'),
|
||||
array('2.2 || 2.0', 'foo/impl2'),
|
||||
array('^3.0', 'foo/replaced'),
|
||||
array('1.1', 'a/provider'),
|
||||
array('1.2 || 1.4', 'a/provider2'),
|
||||
array('2.2', 'b/replacer'),
|
||||
array('3.0', 'c/c'),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider getVersionProvider
|
||||
*/
|
||||
public function testGetVersion($expected, $name)
|
||||
{
|
||||
$this->assertSame($expected, InstalledVersions::getVersion($name));
|
||||
}
|
||||
|
||||
public static function getVersionProvider()
|
||||
{
|
||||
return array(
|
||||
array('dev-master', '__root__'),
|
||||
array(null, 'foo/impl'),
|
||||
array(null, 'foo/impl2'),
|
||||
array(null, 'foo/replaced'),
|
||||
array('1.1.0.0', 'a/provider'),
|
||||
array('1.2.0.0', 'a/provider2'),
|
||||
array('2.2.0.0', 'b/replacer'),
|
||||
array('3.0.0.0', 'c/c'),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider getPrettyVersionProvider
|
||||
*/
|
||||
public function testGetPrettyVersion($expected, $name)
|
||||
{
|
||||
$this->assertSame($expected, InstalledVersions::getPrettyVersion($name));
|
||||
}
|
||||
|
||||
public static function getPrettyVersionProvider()
|
||||
{
|
||||
return array(
|
||||
array('dev-master', '__root__'),
|
||||
array(null, 'foo/impl'),
|
||||
array(null, 'foo/impl2'),
|
||||
array(null, 'foo/replaced'),
|
||||
array('1.1', 'a/provider'),
|
||||
array('1.2', 'a/provider2'),
|
||||
array('2.2', 'b/replacer'),
|
||||
array('3.0', 'c/c'),
|
||||
);
|
||||
}
|
||||
|
||||
public function testGetVersionOutOfBounds()
|
||||
{
|
||||
$this->setExpectedException('OutOfBoundsException');
|
||||
InstalledVersions::getVersion('not/installed');
|
||||
}
|
||||
|
||||
public function testGetRootPackage()
|
||||
{
|
||||
$this->assertSame(array(
|
||||
'pretty_version' => 'dev-master',
|
||||
'version' => 'dev-master',
|
||||
'aliases' => array(
|
||||
'1.10.x-dev',
|
||||
),
|
||||
'reference' => 'sourceref-by-default',
|
||||
'name' => '__root__',
|
||||
), InstalledVersions::getRootPackage());
|
||||
}
|
||||
|
||||
public function testGetRawData()
|
||||
{
|
||||
$this->assertSame(require __DIR__.'/Repository/Fixtures/installed.php', InstalledVersions::getRawData());
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider getReferenceProvider
|
||||
*/
|
||||
public function testGetReference($expected, $name)
|
||||
{
|
||||
$this->assertSame($expected, InstalledVersions::getReference($name));
|
||||
}
|
||||
|
||||
public static function getReferenceProvider()
|
||||
{
|
||||
return array(
|
||||
array('sourceref-by-default', '__root__'),
|
||||
array(null, 'foo/impl'),
|
||||
array(null, 'foo/impl2'),
|
||||
array(null, 'foo/replaced'),
|
||||
array('distref-as-no-source', 'a/provider'),
|
||||
array('distref-as-installed-from-dist', 'a/provider2'),
|
||||
array(null, 'b/replacer'),
|
||||
array(null, 'c/c'),
|
||||
);
|
||||
}
|
||||
}
|
@ -0,0 +1,68 @@
|
||||
<?php return array(
|
||||
'root' => array(
|
||||
'pretty_version' => 'dev-master',
|
||||
'version' => 'dev-master',
|
||||
'aliases' => array(
|
||||
'1.10.x-dev',
|
||||
),
|
||||
'reference' => 'sourceref-by-default',
|
||||
'name' => '__root__',
|
||||
),
|
||||
'versions' => array(
|
||||
'__root__' => array(
|
||||
'pretty_version' => 'dev-master',
|
||||
'version' => 'dev-master',
|
||||
'aliases' => array(
|
||||
'1.10.x-dev',
|
||||
),
|
||||
'reference' => 'sourceref-by-default',
|
||||
),
|
||||
'a/provider' => array(
|
||||
'pretty_version' => '1.1',
|
||||
'version' => '1.1.0.0',
|
||||
'aliases' => array(),
|
||||
'reference' => 'distref-as-no-source',
|
||||
),
|
||||
'a/provider2' => array(
|
||||
'pretty_version' => '1.2',
|
||||
'version' => '1.2.0.0',
|
||||
'aliases' => array(
|
||||
'1.4',
|
||||
),
|
||||
'reference' => 'distref-as-installed-from-dist',
|
||||
),
|
||||
'b/replacer' => array(
|
||||
'pretty_version' => '2.2',
|
||||
'version' => '2.2.0.0',
|
||||
'aliases' => array(),
|
||||
'reference' => NULL,
|
||||
),
|
||||
'c/c' => array(
|
||||
'pretty_version' => '3.0',
|
||||
'version' => '3.0.0.0',
|
||||
'aliases' => array(),
|
||||
'reference' => NULL,
|
||||
),
|
||||
'foo/impl' => array(
|
||||
'provided' => array(
|
||||
'^1.1',
|
||||
'1.2',
|
||||
'1.4',
|
||||
'2.0',
|
||||
)
|
||||
),
|
||||
'foo/impl2' => array(
|
||||
'provided' => array(
|
||||
'2.0',
|
||||
),
|
||||
'replaced' => array(
|
||||
'2.2',
|
||||
),
|
||||
),
|
||||
'foo/replaced' => array(
|
||||
'replaced' => array(
|
||||
'^3.0',
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
Loading…
Reference in New Issue