diff --git a/src/Composer/Command/InitCommand.php b/src/Composer/Command/InitCommand.php
index 795d72958..cd3f7395d 100644
--- a/src/Composer/Command/InitCommand.php
+++ b/src/Composer/Command/InitCommand.php
@@ -321,7 +321,7 @@ EOT
return $this->repos;
}
- protected function determineRequirements(InputInterface $input, OutputInterface $output, $requires = array())
+ protected function determineRequirements(InputInterface $input, OutputInterface $output, $requires = array(), $phpVersion = null)
{
if ($requires) {
$requires = $this->normalizeRequirements($requires);
@@ -331,7 +331,7 @@ EOT
foreach ($requires as $requirement) {
if (!isset($requirement['version'])) {
// determine the best version automatically
- $version = $this->findBestVersionForPackage($input, $requirement['name']);
+ $version = $this->findBestVersionForPackage($input, $requirement['name'], $phpVersion);
$requirement['version'] = $version;
$io->writeError(sprintf(
@@ -426,7 +426,7 @@ EOT
);
if (false === $constraint) {
- $constraint = $this->findBestVersionForPackage($input, $package);
+ $constraint = $this->findBestVersionForPackage($input, $package, $phpVersion);
$io->writeError(sprintf(
'Using version %s for %s',
@@ -591,14 +591,15 @@ EOT
*
* @param InputInterface $input
* @param string $name
+ * @param string $phpVersion
* @throws \InvalidArgumentException
* @return string
*/
- private function findBestVersionForPackage(InputInterface $input, $name)
+ private function findBestVersionForPackage(InputInterface $input, $name, $phpVersion)
{
// find the latest version allowed in this pool
$versionSelector = new VersionSelector($this->getPool($input));
- $package = $versionSelector->findBestCandidate($name);
+ $package = $versionSelector->findBestCandidate($name, null, $phpVersion);
if (!$package) {
throw new \InvalidArgumentException(sprintf(
diff --git a/src/Composer/Command/RequireCommand.php b/src/Composer/Command/RequireCommand.php
index c7bcc8735..79f281595 100644
--- a/src/Composer/Command/RequireCommand.php
+++ b/src/Composer/Command/RequireCommand.php
@@ -96,12 +96,18 @@ EOT
$composer = $this->getComposer();
$repos = $composer->getRepositoryManager()->getRepositories();
+ $platformOverrides = $composer->getConfig()->get('platform') ?: array();
+ // initialize $this->repos as it is used by the parent InitCommand
$this->repos = new CompositeRepository(array_merge(
- array(new PlatformRepository),
+ array(new PlatformRepository(array(), $platformOverrides)),
$repos
));
- $requirements = $this->determineRequirements($input, $output, $input->getArgument('packages'));
+ $phpPackages = $this->repos->findPackages('php');
+ $phpPackage = reset($phpPackages);
+ $phpVersion = $phpPackage->getVersion();
+ unset($phpPackage, $phpPackages);
+ $requirements = $this->determineRequirements($input, $output, $input->getArgument('packages'), $phpVersion);
$requireKey = $input->getOption('dev') ? 'require-dev' : 'require';
$removeKey = $input->getOption('dev') ? 'require' : 'require-dev';
diff --git a/src/Composer/Package/BasePackage.php b/src/Composer/Package/BasePackage.php
index 0369a36dd..3dd390aa0 100644
--- a/src/Composer/Package/BasePackage.php
+++ b/src/Composer/Package/BasePackage.php
@@ -223,6 +223,11 @@ abstract class BasePackage implements PackageInterface
return $this->getPrettyVersion() . ' ' . $this->getSourceReference();
}
+ public function getStabilityPriority()
+ {
+ return self::$stabilities[$this->getStability()];
+ }
+
public function __clone()
{
$this->repository = null;
diff --git a/src/Composer/Package/Version/VersionSelector.php b/src/Composer/Package/Version/VersionSelector.php
index a22cf2c70..03bb62fe7 100644
--- a/src/Composer/Package/Version/VersionSelector.php
+++ b/src/Composer/Package/Version/VersionSelector.php
@@ -17,6 +17,8 @@ use Composer\Package\PackageInterface;
use Composer\Package\Loader\ArrayLoader;
use Composer\Package\Dumper\ArrayDumper;
use Composer\Semver\VersionParser as SemverVersionParser;
+use Composer\Semver\Semver;
+use Composer\Semver\Constraint\Constraint;
/**
* Selects the best possible version for a package
@@ -41,13 +43,22 @@ class VersionSelector
*
* @param string $packageName
* @param string $targetPackageVersion
+ * @param string $targetPhpVersion
* @return PackageInterface|bool
*/
- public function findBestCandidate($packageName, $targetPackageVersion = null)
+ public function findBestCandidate($packageName, $targetPackageVersion = null, $targetPhpVersion = null, $preferStable = true)
{
$constraint = $targetPackageVersion ? $this->getParser()->parseConstraints($targetPackageVersion) : null;
$candidates = $this->pool->whatProvides(strtolower($packageName), $constraint, true);
+ if ($targetPhpVersion) {
+ $phpConstraint = new Constraint('==', $this->getParser()->normalize($targetPhpVersion));
+ $candidates = array_filter($candidates, function ($pkg) use ($phpConstraint) {
+ $reqs = $pkg->getRequires();
+ return !isset($reqs['php']) || $reqs['php']->getConstraint()->matches($phpConstraint);
+ });
+ }
+
if (!$candidates) {
return false;
}
@@ -55,6 +66,9 @@ class VersionSelector
// select highest version if we have many
$package = reset($candidates);
foreach ($candidates as $candidate) {
+ if ($preferStable && $package->getStabilityPriority() < $candidate->getStabilityPriority()) {
+ continue;
+ }
if (version_compare($package->getVersion(), $candidate->getVersion(), '<')) {
$package = $candidate;
}
diff --git a/tests/Composer/Test/Package/Version/VersionSelectorTest.php b/tests/Composer/Test/Package/Version/VersionSelectorTest.php
index a48cc805d..f4d441ee8 100644
--- a/tests/Composer/Test/Package/Version/VersionSelectorTest.php
+++ b/tests/Composer/Test/Package/Version/VersionSelectorTest.php
@@ -13,6 +13,8 @@
namespace Composer\Test\Package\Version;
use Composer\Package\Version\VersionSelector;
+use Composer\Package\Package;
+use Composer\Package\Link;
use Composer\Semver\VersionParser;
class VersionSelectorTest extends \PHPUnit_Framework_TestCase
@@ -25,9 +27,9 @@ class VersionSelectorTest extends \PHPUnit_Framework_TestCase
{
$packageName = 'foobar';
- $package1 = $this->createMockPackage('1.2.1');
- $package2 = $this->createMockPackage('1.2.2');
- $package3 = $this->createMockPackage('1.2.0');
+ $package1 = $this->createPackage('1.2.1');
+ $package2 = $this->createPackage('1.2.2');
+ $package3 = $this->createPackage('1.2.0');
$packages = array($package1, $package2, $package3);
$pool = $this->createMockPool();
@@ -40,7 +42,70 @@ class VersionSelectorTest extends \PHPUnit_Framework_TestCase
$best = $versionSelector->findBestCandidate($packageName);
// 1.2.2 should be returned because it's the latest of the returned versions
- $this->assertEquals($package2, $best, 'Latest version should be 1.2.2');
+ $this->assertSame($package2, $best, 'Latest version should be 1.2.2');
+ }
+
+ public function testLatestVersionIsReturnedThatMatchesPhpRequirement()
+ {
+ $packageName = 'foobar';
+
+ $parser = new VersionParser;
+ $package1 = $this->createPackage('1.0.0');
+ $package2 = $this->createPackage('2.0.0');
+ $package1->setRequires(array('php' => new Link($packageName, 'php', $parser->parseConstraints('>=5.4'), 'requires', '>=5.4')));
+ $package2->setRequires(array('php' => new Link($packageName, 'php', $parser->parseConstraints('>=5.6'), 'requires', '>=5.6')));
+ $packages = array($package1, $package2);
+
+ $pool = $this->createMockPool();
+ $pool->expects($this->once())
+ ->method('whatProvides')
+ ->with($packageName, null, true)
+ ->will($this->returnValue($packages));
+
+ $versionSelector = new VersionSelector($pool);
+ $best = $versionSelector->findBestCandidate($packageName, null, '5.5.0');
+
+ $this->assertSame($package1, $best, 'Latest version supporting php 5.5 should be returned (1.0.0)');
+ }
+
+ public function testMostStableVersionIsReturned()
+ {
+ $packageName = 'foobar';
+
+ $package1 = $this->createPackage('1.0.0');
+ $package2 = $this->createPackage('1.1.0-beta');
+ $packages = array($package1, $package2);
+
+ $pool = $this->createMockPool();
+ $pool->expects($this->once())
+ ->method('whatProvides')
+ ->with($packageName, null, true)
+ ->will($this->returnValue($packages));
+
+ $versionSelector = new VersionSelector($pool);
+ $best = $versionSelector->findBestCandidate($packageName);
+
+ $this->assertSame($package1, $best, 'Latest most stable version should be returned (1.0.0)');
+ }
+
+ public function testHighestVersionIsReturned()
+ {
+ $packageName = 'foobar';
+
+ $package1 = $this->createPackage('1.0.0');
+ $package2 = $this->createPackage('1.1.0-beta');
+ $packages = array($package1, $package2);
+
+ $pool = $this->createMockPool();
+ $pool->expects($this->once())
+ ->method('whatProvides')
+ ->with($packageName, null, true)
+ ->will($this->returnValue($packages));
+
+ $versionSelector = new VersionSelector($pool);
+ $best = $versionSelector->findBestCandidate($packageName, null, null, false);
+
+ $this->assertSame($package2, $best, 'Latest version should be returned (1.1.0-beta)');
}
public function testFalseReturnedOnNoPackages()
@@ -86,7 +151,7 @@ class VersionSelectorTest extends \PHPUnit_Framework_TestCase
$recommended = $versionSelector->findRecommendedRequireVersion($package);
// assert that the recommended version is what we expect
- $this->assertEquals($expectedVersion, $recommended);
+ $this->assertSame($expectedVersion, $recommended);
}
public function getRecommendedRequireVersionPackages()
@@ -124,14 +189,10 @@ class VersionSelectorTest extends \PHPUnit_Framework_TestCase
);
}
- private function createMockPackage($version)
+ private function createPackage($version)
{
- $package = $this->getMock('\Composer\Package\PackageInterface');
- $package->expects($this->any())
- ->method('getVersion')
- ->will($this->returnValue($version));
-
- return $package;
+ $parser = new VersionParser();
+ return new Package('foo', $parser->normalize($version), $version);
}
private function createMockPool()