From 70a3c68f730927ef176a6cdf78c436a51a049e60 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Wed, 18 Apr 2012 01:17:32 +0200 Subject: [PATCH 01/10] Add package stability --- src/Composer/Package/AliasPackage.php | 12 ++++++++- src/Composer/Package/MemoryPackage.php | 11 +++++++- src/Composer/Package/PackageInterface.php | 7 +++++ .../Package/Version/VersionParser.php | 27 ++++++++++++++----- .../Package/Version/VersionParserTest.php | 22 ++++++++------- 5 files changed, 61 insertions(+), 18 deletions(-) diff --git a/src/Composer/Package/AliasPackage.php b/src/Composer/Package/AliasPackage.php index 6670b93e5..6835226d8 100644 --- a/src/Composer/Package/AliasPackage.php +++ b/src/Composer/Package/AliasPackage.php @@ -28,6 +28,7 @@ class AliasPackage extends BasePackage protected $dev; protected $aliasOf; protected $rootPackageAlias = false; + protected $stability; protected $requires; protected $conflicts; @@ -50,7 +51,8 @@ class AliasPackage extends BasePackage $this->version = $version; $this->prettyVersion = $prettyVersion; $this->aliasOf = $aliasOf; - $this->dev = VersionParser::isDev($version); + $this->stability = VersionParser::parseStability($version); + $this->dev = $this->stability === 'dev'; // replace self.version dependencies foreach (array('requires', 'devRequires') as $type) { @@ -91,6 +93,14 @@ class AliasPackage extends BasePackage return $this->version; } + /** + * {@inheritDoc} + */ + public function getStability() + { + return $this->stability; + } + /** * {@inheritDoc} */ diff --git a/src/Composer/Package/MemoryPackage.php b/src/Composer/Package/MemoryPackage.php index 147f94ed7..cc8f9ff1c 100644 --- a/src/Composer/Package/MemoryPackage.php +++ b/src/Composer/Package/MemoryPackage.php @@ -71,7 +71,8 @@ class MemoryPackage extends BasePackage $this->version = $version; $this->prettyVersion = $prettyVersion; - $this->dev = VersionParser::isDev($version); + $this->stability = VersionParser::parseStability($version); + $this->dev = $this->stability === 'dev'; } /** @@ -98,6 +99,14 @@ class MemoryPackage extends BasePackage return $this->type ?: 'library'; } + /** + * {@inheritDoc} + */ + public function getStability() + { + return $this->stability; + } + /** * @param string $targetDir */ diff --git a/src/Composer/Package/PackageInterface.php b/src/Composer/Package/PackageInterface.php index 5b8d2ddb2..43a07d28b 100644 --- a/src/Composer/Package/PackageInterface.php +++ b/src/Composer/Package/PackageInterface.php @@ -180,6 +180,13 @@ interface PackageInterface */ function getPrettyVersion(); + /** + * Returns the stability of this package: one of (dev, alpha, beta, RC, stable) + * + * @return string + */ + function getStability(); + /** * Returns the package license, e.g. MIT, BSD, GPL * diff --git a/src/Composer/Package/Version/VersionParser.php b/src/Composer/Package/Version/VersionParser.php index 57d0596cc..374497ad4 100644 --- a/src/Composer/Package/Version/VersionParser.php +++ b/src/Composer/Package/Version/VersionParser.php @@ -22,17 +22,30 @@ use Composer\Package\LinkConstraint\VersionConstraint; */ class VersionParser { - private $modifierRegex = '[.-]?(?:(beta|RC|alpha|patch|pl|p)(?:[.-]?(\d+))?)?([.-]?dev)?'; + private static $modifierRegex = '[.-]?(?:(beta|RC|alpha|patch|pl|p)(?:[.-]?(\d+))?)?([.-]?dev)?'; /** - * Checks if a version is dev or not + * Returns the stability of a version * * @param string $version - * @return Boolean + * @return string */ - static public function isDev($version) + static public function parseStability($version) { - return 'dev-' === substr($version, 0, 4) || '-dev' === substr($version, -4); + if ('dev-' === substr($version, 0, 4) || '-dev' === substr($version, -4)) { + return 'dev'; + } + + preg_match('{'.self::$modifierRegex.'$}', $version, $match); + if (!empty($match[3])) { + return 'dev'; + } + + if (!empty($match[1]) && ($match[1] === 'beta' || $match[1] === 'alpha' || $match[1] === 'RC')) { + return $match[1]; + } + + return 'stable'; } /** @@ -60,13 +73,13 @@ class VersionParser } // match classical versioning - if (preg_match('{^v?(\d{1,3})(\.\d+)?(\.\d+)?(\.\d+)?'.$this->modifierRegex.'$}i', $version, $matches)) { + if (preg_match('{^v?(\d{1,3})(\.\d+)?(\.\d+)?(\.\d+)?'.self::$modifierRegex.'$}i', $version, $matches)) { $version = $matches[1] .(!empty($matches[2]) ? $matches[2] : '.0') .(!empty($matches[3]) ? $matches[3] : '.0') .(!empty($matches[4]) ? $matches[4] : '.0'); $index = 5; - } elseif (preg_match('{^v?(\d{4}(?:[.:-]?\d{2}){1,6}(?:[.:-]?\d{1,3})?)'.$this->modifierRegex.'$}i', $version, $matches)) { // match date-based versioning + } elseif (preg_match('{^v?(\d{4}(?:[.:-]?\d{2}){1,6}(?:[.:-]?\d{1,3})?)'.self::$modifierRegex.'$}i', $version, $matches)) { // match date-based versioning $version = preg_replace('{\D}', '-', $matches[1]); $index = 2; } diff --git a/tests/Composer/Test/Package/Version/VersionParserTest.php b/tests/Composer/Test/Package/Version/VersionParserTest.php index fa123e7dc..c0eefb14c 100644 --- a/tests/Composer/Test/Package/Version/VersionParserTest.php +++ b/tests/Composer/Test/Package/Version/VersionParserTest.php @@ -195,21 +195,25 @@ class VersionParserTest extends \PHPUnit_Framework_TestCase } /** - * @dataProvider dataIsDev + * @dataProvider stabilityProvider */ - public function testIsDev($expected, $version) + public function testParseStability($expected, $version) { - $this->assertSame($expected, VersionParser::isDev($version)); + $this->assertSame($expected, VersionParser::parseStability($version)); } - public function dataIsDev() + public function stabilityProvider() { return array( - array(false, '1.0'), - array(false, 'v2.0.*'), - array(false, '3.0dev'), - array(true, 'dev-master'), - array(true, '3.1.2-dev'), + array('stable', '1.0'), + array('dev', 'v2.0.x-dev'), + array('RC', '3.0-RC2'), + array('dev', 'dev-master'), + array('dev', '3.1.2-dev'), + array('stable', '3.1.2-pl2'), + array('stable', '3.1.2-patch'), + array('alpha', '3.1.2-alpha5'), + array('beta', '3.1.2-beta'), ); } } From b0134b56c519ba8148c2d256f44733c7a831737c Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Wed, 18 Apr 2012 01:29:19 +0200 Subject: [PATCH 02/10] Add CompositeRepo::getRepositories --- src/Composer/Repository/CompositeRepository.php | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/Composer/Repository/CompositeRepository.php b/src/Composer/Repository/CompositeRepository.php index e000d97e8..82b7e9b45 100644 --- a/src/Composer/Repository/CompositeRepository.php +++ b/src/Composer/Repository/CompositeRepository.php @@ -36,6 +36,16 @@ class CompositeRepository implements RepositoryInterface $this->repositories = $repositories; } + /** + * Returns all the wrapped repositories + * + * @return array + */ + public function getRepositories() + { + return $this->repositories; + } + /** * {@inheritdoc} */ From 66068fedcb49cbab314167c556a30bca814794f0 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Wed, 18 Apr 2012 01:33:47 +0200 Subject: [PATCH 03/10] Add minimum-stability flag on root package to filter packages by stability --- res/composer-schema.json | 4 ++ src/Composer/DependencyResolver/Pool.php | 43 ++++++++++++++++--- src/Composer/Installer.php | 2 +- .../Package/Loader/RootPackageLoader.php | 4 ++ src/Composer/Package/MemoryPackage.php | 20 +++++++++ 5 files changed, 66 insertions(+), 7 deletions(-) diff --git a/res/composer-schema.json b/res/composer-schema.json index f1f7ed1f9..35377faff 100644 --- a/res/composer-schema.json +++ b/res/composer-schema.json @@ -143,6 +143,10 @@ "description": "A set of additional repositories where packages can be found.", "additionalProperties": true }, + "minimum-stability": { + "type": ["string"], + "description": "The minimum stability the packages must have to be install-able. Possible values are: dev, alpha, beta, RC, stable." + }, "bin": { "type": ["array"], "description": "A set of files that should be treated as binaries and symlinked into bin-dir (from config).", diff --git a/src/Composer/DependencyResolver/Pool.php b/src/Composer/DependencyResolver/Pool.php index 24ef5427a..df24a8b26 100644 --- a/src/Composer/DependencyResolver/Pool.php +++ b/src/Composer/DependencyResolver/Pool.php @@ -14,17 +14,35 @@ namespace Composer\DependencyResolver; use Composer\Package\LinkConstraint\LinkConstraintInterface; use Composer\Repository\RepositoryInterface; +use Composer\Repository\CompositeRepository; +use Composer\Repository\InstalledRepositoryInterface; +use Composer\Repository\PlatformRepository; /** * A package pool contains repositories that provide packages. * * @author Nils Adermann + * @author Jordi Boggiano */ class Pool { protected $repositories = array(); protected $packages = array(); protected $packageByName = array(); + protected $acceptableStabilities; + + public function __construct($minimumStability = 'dev') + { + $stabilities = array( + 'stable', + 'RC', + 'beta', + 'alpha', + 'dev', + ); + + $this->acceptableStabilities = array_flip(array_splice($stabilities, 0, array_search($minimumStability, $stabilities) + 1)); + } /** * Adds a repository and its packages to this package pool @@ -33,14 +51,27 @@ class Pool */ public function addRepository(RepositoryInterface $repo) { - $this->repositories[] = $repo; + if ($repo instanceof CompositeRepository) { + $repos = $repo->getRepositories(); + } else { + $repos = array($repo); + } + + foreach ($repos as $repo) { + $this->repositories[] = $repo; + + $exempt = $repo instanceof PlatformRepository || $repo instanceof InstalledRepositoryInterface; + foreach ($repo->getPackages() as $package) { + if (!$exempt && !isset($this->acceptableStabilities[$package->getStability()])) { + continue; + } - foreach ($repo->getPackages() as $package) { - $package->setId(count($this->packages) + 1); - $this->packages[] = $package; + $package->setId(count($this->packages) + 1); + $this->packages[] = $package; - foreach ($package->getNames() as $name) { - $this->packageByName[$name][] = $package; + foreach ($package->getNames() as $name) { + $this->packageByName[$name][] = $package; + } } } } diff --git a/src/Composer/Installer.php b/src/Composer/Installer.php index 2f7a3b137..097c70529 100644 --- a/src/Composer/Installer.php +++ b/src/Composer/Installer.php @@ -207,7 +207,7 @@ class Installer } // creating repository pool - $pool = new Pool; + $pool = new Pool($this->package->getMinimumStability()); $pool->addRepository($installedRepo); foreach ($this->repositoryManager->getRepositories() as $repository) { $pool->addRepository($repository); diff --git a/src/Composer/Package/Loader/RootPackageLoader.php b/src/Composer/Package/Loader/RootPackageLoader.php index 496463222..45c8f25e8 100644 --- a/src/Composer/Package/Loader/RootPackageLoader.php +++ b/src/Composer/Package/Loader/RootPackageLoader.php @@ -76,6 +76,10 @@ class RootPackageLoader extends ArrayLoader $package->setAliases($aliases); } + if (isset($config['minimum-stability'])) { + $package->setMinimumStability($config['minimum-stability']); + } + if (isset($config['repositories'])) { foreach ($config['repositories'] as $index => $repo) { if (isset($repo['packagist']) && $repo['packagist'] === false) { diff --git a/src/Composer/Package/MemoryPackage.php b/src/Composer/Package/MemoryPackage.php index cc8f9ff1c..3f701cd31 100644 --- a/src/Composer/Package/MemoryPackage.php +++ b/src/Composer/Package/MemoryPackage.php @@ -48,6 +48,8 @@ class MemoryPackage extends BasePackage protected $prettyAlias; protected $dev; + protected $minimumStability = 'dev'; + protected $requires = array(); protected $conflicts = array(); protected $provides = array(); @@ -597,6 +599,24 @@ class MemoryPackage extends BasePackage return $this->homepage; } + /** + * Set the minimumStability + * + * @param string $minimumStability + */ + public function setMinimumStability($minimumStability) + { + $this->minimumStability = $minimumStability; + } + + /** + * {@inheritDoc} + */ + public function getMinimumStability() + { + return $this->minimumStability; + } + /** * Set the autoload mapping * From 1aaae5284b7a3a21b17cb408e5dd6ef91c9130e5 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Mon, 23 Apr 2012 23:51:57 +0200 Subject: [PATCH 04/10] Add list of stabilities to base package --- src/Composer/Package/BasePackage.php | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/Composer/Package/BasePackage.php b/src/Composer/Package/BasePackage.php index 171c5ee08..b3d797395 100644 --- a/src/Composer/Package/BasePackage.php +++ b/src/Composer/Package/BasePackage.php @@ -32,6 +32,14 @@ abstract class BasePackage implements PackageInterface 'require-dev' => array('description' => 'requires (for development)', 'method' => 'devRequires'), ); + public static $stabilities = array( + 'stable', + 'RC', + 'beta', + 'alpha', + 'dev', + ); + protected $name; protected $prettyName; From ef28f3b067adbc8f6e2aeea6ee02201b6c8aa767 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Mon, 23 Apr 2012 23:53:34 +0200 Subject: [PATCH 05/10] Basic handling of stability flags --- src/Composer/DependencyResolver/Pool.php | 43 +++++++------ src/Composer/Installer.php | 2 +- .../Package/Loader/RootPackageLoader.php | 61 ++++++++++++++----- src/Composer/Package/MemoryPackage.php | 19 ++++++ .../Package/Version/VersionParser.php | 12 ++++ .../Package/Version/VersionParserTest.php | 6 ++ 6 files changed, 110 insertions(+), 33 deletions(-) diff --git a/src/Composer/DependencyResolver/Pool.php b/src/Composer/DependencyResolver/Pool.php index df24a8b26..c52eb67e5 100644 --- a/src/Composer/DependencyResolver/Pool.php +++ b/src/Composer/DependencyResolver/Pool.php @@ -12,6 +12,7 @@ namespace Composer\DependencyResolver; +use Composer\Package\BasePackage; use Composer\Package\LinkConstraint\LinkConstraintInterface; use Composer\Repository\RepositoryInterface; use Composer\Repository\CompositeRepository; @@ -30,18 +31,13 @@ class Pool protected $packages = array(); protected $packageByName = array(); protected $acceptableStabilities; + protected $stabilityFlags; - public function __construct($minimumStability = 'dev') + public function __construct($minimumStability = 'dev', array $stabilityFlags = array()) { - $stabilities = array( - 'stable', - 'RC', - 'beta', - 'alpha', - 'dev', - ); - + $stabilities = BasePackage::$stabilities; $this->acceptableStabilities = array_flip(array_splice($stabilities, 0, array_search($minimumStability, $stabilities) + 1)); + $this->stabilityFlags = $stabilityFlags; } /** @@ -57,20 +53,31 @@ class Pool $repos = array($repo); } + $id = count($this->packages) + 1; foreach ($repos as $repo) { $this->repositories[] = $repo; $exempt = $repo instanceof PlatformRepository || $repo instanceof InstalledRepositoryInterface; foreach ($repo->getPackages() as $package) { - if (!$exempt && !isset($this->acceptableStabilities[$package->getStability()])) { - continue; - } - - $package->setId(count($this->packages) + 1); - $this->packages[] = $package; - - foreach ($package->getNames() as $name) { - $this->packageByName[$name][] = $package; + $name = $package->getName(); + $stability = $package->getStability(); + if ( + // always allow exempt repos + $exempt + // allow if package matches the global stability requirement and has no exception + || (!isset($this->stabilityFlags[$name]) + && isset($this->acceptableStabilities[$stability])) + // allow if package matches the package-specific stability flag + || (isset($this->stabilityFlags[$name]) + && array_search($stability, BasePackage::$stabilities) <= $this->stabilityFlags[$name] + ) + ) { + $package->setId($id++); + $this->packages[] = $package; + + foreach ($package->getNames() as $name) { + $this->packageByName[$name][] = $package; + } } } } diff --git a/src/Composer/Installer.php b/src/Composer/Installer.php index 097c70529..62e746234 100644 --- a/src/Composer/Installer.php +++ b/src/Composer/Installer.php @@ -207,7 +207,7 @@ class Installer } // creating repository pool - $pool = new Pool($this->package->getMinimumStability()); + $pool = new Pool($this->package->getMinimumStability(), $this->package->getStabilityFlags()); $pool->addRepository($installedRepo); foreach ($this->repositoryManager->getRepositories() as $repository) { $pool->addRepository($repository); diff --git a/src/Composer/Package/Loader/RootPackageLoader.php b/src/Composer/Package/Loader/RootPackageLoader.php index 45c8f25e8..f7b313bcb 100644 --- a/src/Composer/Package/Loader/RootPackageLoader.php +++ b/src/Composer/Package/Loader/RootPackageLoader.php @@ -12,6 +12,7 @@ namespace Composer\Package\Loader; +use Composer\Package\BasePackage; use Composer\Package\Version\VersionParser; use Composer\Repository\RepositoryManager; use Composer\Util\ProcessExecutor; @@ -60,24 +61,22 @@ class RootPackageLoader extends ArrayLoader $package = parent::load($config); + $aliases = array(); + $stabilityFlags = array(); if (isset($config['require'])) { - $aliases = array(); - foreach ($config['require'] as $reqName => $reqVersion) { - if (preg_match('{^([^,\s]+) +as +([^,\s]+)$}', $reqVersion, $match)) { - $aliases[] = array( - 'package' => strtolower($reqName), - 'version' => $this->versionParser->normalize($match[1]), - 'alias' => $match[2], - 'alias_normalized' => $this->versionParser->normalize($match[2]), - ); - } - } - - $package->setAliases($aliases); + $aliases = $this->extractAliases($config['require'], $aliases); + $stabilityFlags = $this->extractStabilityFlags($config['require'], $stabilityFlags); } + if (isset($config['require-dev'])) { + $aliases = $this->extractAliases($config['require-dev'], $aliases); + $stabilityFlags = $this->extractStabilityFlags($config['require-dev'], $stabilityFlags); + } + + $package->setAliases($aliases); + $package->setStabilityFlags($stabilityFlags); if (isset($config['minimum-stability'])) { - $package->setMinimumStability($config['minimum-stability']); + $package->setMinimumStability(VersionParser::normalizeStability($config['minimum-stability'])); } if (isset($config['repositories'])) { @@ -99,4 +98,38 @@ class RootPackageLoader extends ArrayLoader return $package; } + + private function extractAliases(array $requires, array $aliases) + { + foreach ($requires as $reqName => $reqVersion) { + if (preg_match('{^([^,\s]+) +as +([^,\s]+)$}', $reqVersion, $match)) { + $aliases[] = array( + 'package' => strtolower($reqName), + 'version' => $this->versionParser->normalize($match[1]), + 'alias' => $match[2], + 'alias_normalized' => $this->versionParser->normalize($match[2]), + ); + } + } + + return $aliases; + } + + private function extractStabilityFlags(array $requires, array $stabilityFlags) + { + $stabilities = BasePackage::$stabilities; + foreach ($requires as $reqName => $reqVersion) { + if (preg_match('{^[^,\s]*?@('.implode('|', $stabilities).')$}i', $reqVersion, $match)) { + $name = strtolower($reqName); + $stability = array_search(VersionParser::normalizeStability($match[1]), $stabilities); + + if (isset($stabilityFlags[$name]) && $stabilityFlags[$name] > $stability) { + continue; + } + $stabilityFlags[$name] = $stability; + } + } + + return $stabilityFlags; + } } diff --git a/src/Composer/Package/MemoryPackage.php b/src/Composer/Package/MemoryPackage.php index 3f701cd31..a75c69dc5 100644 --- a/src/Composer/Package/MemoryPackage.php +++ b/src/Composer/Package/MemoryPackage.php @@ -49,6 +49,7 @@ class MemoryPackage extends BasePackage protected $dev; protected $minimumStability = 'dev'; + protected $stabilityFlags = array(); protected $requires = array(); protected $conflicts = array(); @@ -617,6 +618,24 @@ class MemoryPackage extends BasePackage return $this->minimumStability; } + /** + * Set the stabilityFlags + * + * @param array $stabilityFlags + */ + public function setStabilityFlags(array $stabilityFlags) + { + $this->stabilityFlags = $stabilityFlags; + } + + /** + * {@inheritDoc} + */ + public function getStabilityFlags() + { + return $this->stabilityFlags; + } + /** * Set the autoload mapping * diff --git a/src/Composer/Package/Version/VersionParser.php b/src/Composer/Package/Version/VersionParser.php index 374497ad4..e7a510769 100644 --- a/src/Composer/Package/Version/VersionParser.php +++ b/src/Composer/Package/Version/VersionParser.php @@ -12,6 +12,7 @@ namespace Composer\Package\Version; +use Composer\Package\BasePackage; use Composer\Package\LinkConstraint\MultiConstraint; use Composer\Package\LinkConstraint\VersionConstraint; @@ -48,6 +49,13 @@ class VersionParser return 'stable'; } + static public function normalizeStability($stability) + { + $stability = strtolower($stability); + + return $stability === 'rc' ? 'RC' : $stability; + } + /** * Normalizes a version string to be able to perform comparisons on it * @@ -143,6 +151,10 @@ class VersionParser */ public function parseConstraints($constraints) { + if (preg_match('{^([^,\s]*?)@('.implode('|', BasePackage::$stabilities).')$}i', $constraints, $match)) { + $constraints = empty($match[1]) ? '*' : $match[1]; + } + $constraints = preg_split('{\s*,\s*}', trim($constraints)); if (count($constraints) > 1) { diff --git a/tests/Composer/Test/Package/Version/VersionParserTest.php b/tests/Composer/Test/Package/Version/VersionParserTest.php index c0eefb14c..0be1b7269 100644 --- a/tests/Composer/Test/Package/Version/VersionParserTest.php +++ b/tests/Composer/Test/Package/Version/VersionParserTest.php @@ -106,6 +106,12 @@ class VersionParserTest extends \PHPUnit_Framework_TestCase ); } + public function testParseConstraintsIgnoresStabilityFlag() + { + $parser = new VersionParser; + $this->assertSame((string) new VersionConstraint('=', '1.0.0.0'), (string) $parser->parseConstraints('1.0@dev')); + } + /** * @dataProvider simpleConstraints */ From fabf5c2f6d816a3f3dbc453119f7603670275437 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Wed, 9 May 2012 20:03:19 +0200 Subject: [PATCH 06/10] Add InstalledArrayRepository to make sure the root package is not purged by the Pool because of a lower stability --- src/Composer/Installer.php | 3 +- .../Repository/InstalledArrayRepository.php | 42 +++++++++++++++++++ 2 files changed, 44 insertions(+), 1 deletion(-) create mode 100644 src/Composer/Repository/InstalledArrayRepository.php diff --git a/src/Composer/Installer.php b/src/Composer/Installer.php index 62e746234..0ad433c90 100644 --- a/src/Composer/Installer.php +++ b/src/Composer/Installer.php @@ -29,6 +29,7 @@ use Composer\Package\Locker; use Composer\Package\PackageInterface; use Composer\Repository\ArrayRepository; use Composer\Repository\CompositeRepository; +use Composer\Repository\InstalledArrayRepository; use Composer\Repository\PlatformRepository; use Composer\Repository\RepositoryInterface; use Composer\Repository\RepositoryManager; @@ -139,7 +140,7 @@ class Installer $repos = array_merge( $this->repositoryManager->getLocalRepositories(), array( - new ArrayRepository(array($this->package)), + new InstalledArrayRepository(array($this->package)), new PlatformRepository(), ) ); diff --git a/src/Composer/Repository/InstalledArrayRepository.php b/src/Composer/Repository/InstalledArrayRepository.php new file mode 100644 index 000000000..151d00190 --- /dev/null +++ b/src/Composer/Repository/InstalledArrayRepository.php @@ -0,0 +1,42 @@ + + * Jordi Boggiano + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Composer\Repository; + +use Composer\Json\JsonFile; +use Composer\Package\PackageInterface; +use Composer\Package\Loader\ArrayLoader; +use Composer\Package\Dumper\ArrayDumper; + +/** + * Installed array repository. + * + * This is used for serving the RootPackage inside an in-memory InstalledRepository + * + * @author Jordi Boggiano + */ +class InstalledArrayRepository extends ArrayRepository implements InstalledRepositoryInterface +{ + /** + * {@inheritDoc} + */ + public function write() + { + } + + /** + * {@inheritDoc} + */ + public function reload() + { + } +} From a3018c43eb9e8f6661b16e07d7220b062458ac44 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Wed, 9 May 2012 20:03:56 +0200 Subject: [PATCH 07/10] Infer stability flags for requirements that have an explicit version required of a lower stability --- src/Composer/Package/Loader/RootPackageLoader.php | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/Composer/Package/Loader/RootPackageLoader.php b/src/Composer/Package/Loader/RootPackageLoader.php index f7b313bcb..c5cf446b5 100644 --- a/src/Composer/Package/Loader/RootPackageLoader.php +++ b/src/Composer/Package/Loader/RootPackageLoader.php @@ -119,6 +119,7 @@ class RootPackageLoader extends ArrayLoader { $stabilities = BasePackage::$stabilities; foreach ($requires as $reqName => $reqVersion) { + // parse explicit stability flags if (preg_match('{^[^,\s]*?@('.implode('|', $stabilities).')$}i', $reqVersion, $match)) { $name = strtolower($reqName); $stability = array_search(VersionParser::normalizeStability($match[1]), $stabilities); @@ -127,6 +128,18 @@ class RootPackageLoader extends ArrayLoader continue; } $stabilityFlags[$name] = $stability; + + continue; + } + + // infer flags for requirements that have an explicit -dev or -beta version specified for example + if (preg_match('{^[^,\s@]+$}', $reqVersion) && 'stable' !== ($stabilityName = VersionParser::parseStability($reqVersion))) { + $name = strtolower($reqName); + $stability = array_search($stabilityName, $stabilities); + if (isset($stabilityFlags[$name]) && $stabilityFlags[$name] > $stability) { + continue; + } + $stabilityFlags[$name] = $stability; } } From 0936670213f69f70b50cfe1d4b11f21b754a1993 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Fri, 11 May 2012 17:20:10 +0200 Subject: [PATCH 08/10] Add support for stabilities in lock file --- src/Composer/Installer.php | 11 +++++-- src/Composer/Package/Locker.php | 36 ++++++++++++++++------ tests/Composer/Test/Package/LockerTest.php | 6 ++-- 3 files changed, 40 insertions(+), 13 deletions(-) diff --git a/src/Composer/Installer.php b/src/Composer/Installer.php index 0ad433c90..0eed0050a 100644 --- a/src/Composer/Installer.php +++ b/src/Composer/Installer.php @@ -180,7 +180,9 @@ class Installer $updatedLock = $this->locker->setLockData( $this->repositoryManager->getLocalRepository()->getPackages(), $this->devMode ? $this->repositoryManager->getLocalDevRepository()->getPackages() : null, - $aliases + $aliases, + $this->package->getMinimumStability(), + $this->package->getStabilityFlags() ); if ($updatedLock) { $this->io->write('Writing lock file'); @@ -202,13 +204,18 @@ class Installer protected function doInstall($localRepo, $installedRepo, $aliases, $devMode = false) { + $minimumStability = $this->package->getMinimumStability(); + $stabilityFlags = $this->package->getStabilityFlags(); + // initialize locker to create aliased packages if (!$this->update && $this->locker->isLocked($devMode)) { $lockedPackages = $this->locker->getLockedPackages($devMode); + $minimumStability = $this->locker->getMinimumStability(); + $stabilityFlags = $this->locker->getStabilityFlags(); } // creating repository pool - $pool = new Pool($this->package->getMinimumStability(), $this->package->getStabilityFlags()); + $pool = new Pool($minimumStability, $stabilityFlags); $pool->addRepository($installedRepo); foreach ($this->repositoryManager->getRepositories() as $repository) { $pool->addRepository($repository); diff --git a/src/Composer/Package/Locker.php b/src/Composer/Package/Locker.php index 7f6b738f6..d7502bfdb 100644 --- a/src/Composer/Package/Locker.php +++ b/src/Composer/Package/Locker.php @@ -82,10 +82,10 @@ class Locker */ public function getLockedPackages($dev = false) { - $lockList = $this->getLockData(); + $lockData = $this->getLockData(); $packages = array(); - $lockedPackages = $dev ? $lockList['packages-dev'] : $lockList['packages']; + $lockedPackages = $dev ? $lockData['packages-dev'] : $lockData['packages']; $repo = $dev ? $this->repositoryManager->getLocalDevRepository() : $this->repositoryManager->getLocalRepository(); foreach ($lockedPackages as $info) { @@ -128,22 +128,38 @@ class Locker return $packages; } + public function getMinimumStability() + { + $lockData = $this->getLockData(); + + // TODO BC change dev to stable end of june? + return isset($lockData['minimum-stability']) ? $lockData['minimum-stability'] : 'dev'; + } + + public function getStabilityFlags() + { + $lockData = $this->getLockData(); + + return isset($lockData['stability-flags']) ? $lockData['stability-flags'] : array(); + } + public function getAliases() { - $lockList = $this->getLockData(); - return isset($lockList['aliases']) ? $lockList['aliases'] : array(); + $lockData = $this->getLockData(); + + return isset($lockData['aliases']) ? $lockData['aliases'] : array(); } public function getLockData() { - if (!$this->lockFile->exists()) { - throw new \LogicException('No lockfile found. Unable to read locked packages'); - } - if (null !== $this->lockDataCache) { return $this->lockDataCache; } + if (!$this->lockFile->exists()) { + throw new \LogicException('No lockfile found. Unable to read locked packages'); + } + return $this->lockDataCache = $this->lockFile->read(); } @@ -156,13 +172,15 @@ class Locker * * @return Boolean */ - public function setLockData(array $packages, $devPackages, array $aliases) + public function setLockData(array $packages, $devPackages, array $aliases, $minimumStability, array $stabilityFlags) { $lock = array( 'hash' => $this->hash, 'packages' => null, 'packages-dev' => null, 'aliases' => $aliases, + 'minimum-stability' => $minimumStability, + 'stability-flags' => $stabilityFlags, ); $lock['packages'] = $this->lockPackages($packages); diff --git a/tests/Composer/Test/Package/LockerTest.php b/tests/Composer/Test/Package/LockerTest.php index 729da4b93..a67beb277 100644 --- a/tests/Composer/Test/Package/LockerTest.php +++ b/tests/Composer/Test/Package/LockerTest.php @@ -157,9 +157,11 @@ class LockerTest extends \PHPUnit_Framework_TestCase ), 'packages-dev' => array(), 'aliases' => array(), + 'minimum-stability' => 'dev', + 'stability-flags' => array(), )); - $locker->setLockData(array($package1, $package2), array(), array()); + $locker->setLockData(array($package1, $package2), array(), array(), 'dev', array()); } public function testLockBadPackages() @@ -177,7 +179,7 @@ class LockerTest extends \PHPUnit_Framework_TestCase $this->setExpectedException('LogicException'); - $locker->setLockData(array($package1), array(), array()); + $locker->setLockData(array($package1), array(), array(), 'dev', array()); } public function testIsFresh() From 453b9a616b16865f46dc9ceb7f8f570860d44f87 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Fri, 11 May 2012 17:21:02 +0200 Subject: [PATCH 09/10] Add test for Pool handling and refactor a couple things --- src/Composer/DependencyResolver/Pool.php | 10 ++++++++-- src/Composer/Package/BasePackage.php | 16 +++++++++++----- .../Package/Loader/RootPackageLoader.php | 6 +++--- src/Composer/Package/MemoryPackage.php | 1 + src/Composer/Package/Version/VersionParser.php | 2 +- .../Test/DependencyResolver/PoolTest.php | 17 +++++++++++++++++ 6 files changed, 41 insertions(+), 11 deletions(-) diff --git a/src/Composer/DependencyResolver/Pool.php b/src/Composer/DependencyResolver/Pool.php index c52eb67e5..2b8dec0ad 100644 --- a/src/Composer/DependencyResolver/Pool.php +++ b/src/Composer/DependencyResolver/Pool.php @@ -33,10 +33,16 @@ class Pool protected $acceptableStabilities; protected $stabilityFlags; + // TODO BC change to stable end of june? public function __construct($minimumStability = 'dev', array $stabilityFlags = array()) { $stabilities = BasePackage::$stabilities; - $this->acceptableStabilities = array_flip(array_splice($stabilities, 0, array_search($minimumStability, $stabilities) + 1)); + $this->acceptableStabilities = array(); + foreach (BasePackage::$stabilities as $stability => $value) { + if ($value <= BasePackage::$stabilities[$minimumStability]) { + $this->acceptableStabilities[$stability] = $value; + } + } $this->stabilityFlags = $stabilityFlags; } @@ -69,7 +75,7 @@ class Pool && isset($this->acceptableStabilities[$stability])) // allow if package matches the package-specific stability flag || (isset($this->stabilityFlags[$name]) - && array_search($stability, BasePackage::$stabilities) <= $this->stabilityFlags[$name] + && BasePackage::$stabilities[$stability] <= $this->stabilityFlags[$name] ) ) { $package->setId($id++); diff --git a/src/Composer/Package/BasePackage.php b/src/Composer/Package/BasePackage.php index b3d797395..299c9b17c 100644 --- a/src/Composer/Package/BasePackage.php +++ b/src/Composer/Package/BasePackage.php @@ -32,12 +32,18 @@ abstract class BasePackage implements PackageInterface 'require-dev' => array('description' => 'requires (for development)', 'method' => 'devRequires'), ); + const STABILITY_STABLE = 0; + const STABILITY_RC = 5; + const STABILITY_BETA = 10; + const STABILITY_ALPHA = 15; + const STABILITY_DEV = 20; + public static $stabilities = array( - 'stable', - 'RC', - 'beta', - 'alpha', - 'dev', + 'stable' => self::STABILITY_STABLE, + 'RC' => self::STABILITY_RC, + 'beta' => self::STABILITY_BETA, + 'alpha' => self::STABILITY_ALPHA, + 'dev' => self::STABILITY_DEV, ); protected $name; diff --git a/src/Composer/Package/Loader/RootPackageLoader.php b/src/Composer/Package/Loader/RootPackageLoader.php index c5cf446b5..1742b5632 100644 --- a/src/Composer/Package/Loader/RootPackageLoader.php +++ b/src/Composer/Package/Loader/RootPackageLoader.php @@ -120,9 +120,9 @@ class RootPackageLoader extends ArrayLoader $stabilities = BasePackage::$stabilities; foreach ($requires as $reqName => $reqVersion) { // parse explicit stability flags - if (preg_match('{^[^,\s]*?@('.implode('|', $stabilities).')$}i', $reqVersion, $match)) { + if (preg_match('{^[^,\s]*?@('.implode('|', array_keys($stabilities)).')$}i', $reqVersion, $match)) { $name = strtolower($reqName); - $stability = array_search(VersionParser::normalizeStability($match[1]), $stabilities); + $stability = $stabilities[VersionParser::normalizeStability($match[1])]; if (isset($stabilityFlags[$name]) && $stabilityFlags[$name] > $stability) { continue; @@ -135,7 +135,7 @@ class RootPackageLoader extends ArrayLoader // infer flags for requirements that have an explicit -dev or -beta version specified for example if (preg_match('{^[^,\s@]+$}', $reqVersion) && 'stable' !== ($stabilityName = VersionParser::parseStability($reqVersion))) { $name = strtolower($reqName); - $stability = array_search($stabilityName, $stabilities); + $stability = $stabilities[$stabilityName]; if (isset($stabilityFlags[$name]) && $stabilityFlags[$name] > $stability) { continue; } diff --git a/src/Composer/Package/MemoryPackage.php b/src/Composer/Package/MemoryPackage.php index a75c69dc5..dcb973a5b 100644 --- a/src/Composer/Package/MemoryPackage.php +++ b/src/Composer/Package/MemoryPackage.php @@ -48,6 +48,7 @@ class MemoryPackage extends BasePackage protected $prettyAlias; protected $dev; + // TODO BC change dev to stable end of june? protected $minimumStability = 'dev'; protected $stabilityFlags = array(); diff --git a/src/Composer/Package/Version/VersionParser.php b/src/Composer/Package/Version/VersionParser.php index e7a510769..eedd5f3ae 100644 --- a/src/Composer/Package/Version/VersionParser.php +++ b/src/Composer/Package/Version/VersionParser.php @@ -151,7 +151,7 @@ class VersionParser */ public function parseConstraints($constraints) { - if (preg_match('{^([^,\s]*?)@('.implode('|', BasePackage::$stabilities).')$}i', $constraints, $match)) { + if (preg_match('{^([^,\s]*?)@('.implode('|', array_keys(BasePackage::$stabilities)).')$}i', $constraints, $match)) { $constraints = empty($match[1]) ? '*' : $match[1]; } diff --git a/tests/Composer/Test/DependencyResolver/PoolTest.php b/tests/Composer/Test/DependencyResolver/PoolTest.php index c98570534..7c6618582 100644 --- a/tests/Composer/Test/DependencyResolver/PoolTest.php +++ b/tests/Composer/Test/DependencyResolver/PoolTest.php @@ -14,6 +14,7 @@ namespace Composer\Test\DependencyResolver; use Composer\DependencyResolver\Pool; use Composer\Repository\ArrayRepository; +use Composer\Package\BasePackage; use Composer\Test\TestCase; class PoolTest extends TestCase @@ -31,6 +32,22 @@ class PoolTest extends TestCase $this->assertEquals(array($package), $pool->whatProvides('foo')); } + public function testPoolIgnoresIrrelevantPackages() + { + $pool = new Pool('stable', array('bar' => BasePackage::STABILITY_BETA)); + $repo = new ArrayRepository; + $repo->addPackage($package = $this->getPackage('bar', '1')); + $repo->addPackage($betaPackage = $this->getPackage('bar', '1-beta')); + $repo->addPackage($alphaPackage = $this->getPackage('bar', '1-alpha')); + $repo->addPackage($package2 = $this->getPackage('foo', '1')); + $repo->addPackage($rcPackage2 = $this->getPackage('foo', '1rc')); + + $pool->addRepository($repo); + + $this->assertEquals(array($package, $betaPackage), $pool->whatProvides('bar')); + $this->assertEquals(array($package2), $pool->whatProvides('foo')); + } + /** * @expectedException \RuntimeException */ From 44fdc05e45c60d3ba24d67e90ef3de457c6e5024 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Sat, 12 May 2012 12:08:35 +0200 Subject: [PATCH 10/10] Add stability docs --- doc/04-schema.md | 87 ++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 70 insertions(+), 17 deletions(-) diff --git a/doc/04-schema.md b/doc/04-schema.md index fe91cf4ed..65304ef99 100644 --- a/doc/04-schema.md +++ b/doc/04-schema.md @@ -168,22 +168,10 @@ An example: Optional, but highly recommended. -### Package links (require, require-dev, conflict, replace, provide) - -Each of these takes an object which maps package names to version constraints. - -* **require:** Packages required by this package. -* **require-dev:** Packages required for developing this package, or running - tests, etc. They are installed if install or update is ran with `--dev`. -* **conflict:** Mark this version of this package as conflicting with other - packages. -* **replace:** Packages that can be replaced by this package. This is useful - for large repositories with subtree splits. It allows the main package to - replace all of it's child packages. -* **provide:** List of other packages that are provided by this package. This - is mostly useful for common interfaces. A package could depend on some virtual - `logger` package, any library that provides this logger, would simply list it - in `provide`. +### Package links + +All of the following take an object which maps package names to +[version constraints](01-basic-usage.md#package-versions). Example: @@ -193,7 +181,59 @@ Example: } } -Optional. +All links are optional fields. + +`require` and `require-dev` additionally support stability flags (root-only). +These allow you to further restrict or expand the stability of a package beyond +the scope of the [minimum-stability](#minimum-stability) setting. You can apply +them to a constraint, or just apply them to an empty constraint if you want to +allow unstable packages of a dependency's dependency for example. + +Example: + + { + "require": { + "monolog/monolog": "1.0.*@beta" + "acme/foo": "@dev" + } + } + +#### require + +Lists packages required by this package. The package will not be installed +unless those requirements can be met. + +#### require-dev + +Lists packages required for developing this package, or running +tests, etc. They are installed if install or update is ran with `--dev`. + +#### conflict + +Lists packages that conflict with this version of this package. They +will not be allowed to be installed together with your package. + +#### replace + +Lists packages that are replaced by this package. + +This is useful for packages that contain sub-packages, for example the main +symfony/symfony package contains all the Symfony Components which are also +available as individual packages. If you require the main package it will +automatically fulfill any requirement of one of the individual components, +since it replaces them. + +Caution is advised when using replace however, for the sub-package example +above you should typically only replace using `self.version` as a version +constraint, to make sure the main package only replaces the sub-packages of +that exact version, and not any other version, which would be incorrect. + +#### provide + +List of other packages that are provided by this package. This is mostly +useful for common interfaces. A package could depend on some virtual +`logger` package, any library that implements this logger interface would +simply list it in `provide`. ### suggest @@ -306,6 +346,19 @@ To do that, `autoload` and `target-dir` are defined as follows: Optional. +### minimum-stability (root-only) + +This defines the default behavior for filtering packages by stability. This +defaults to `dev` but in the future will be switched to `stable`. As such if +you rely on a default of `dev` you should specify it in your file to avoid +surprises. + +All versions of each package is checked for stability, and those that are less +stable than the `minimum-stability` setting will be ignored when resolving +your project dependencies. Specific changes to the stability requirements of +a given package can be done in `require` or `require-dev` (see +[package links](#package-links)). + ### repositories (root-only) Custom package repositories to use.