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 */