diff --git a/src/Composer/Package/LinkConstraint/MultiConstraint.php b/src/Composer/Package/LinkConstraint/MultiConstraint.php index 836d565a0..f2eff93ec 100644 --- a/src/Composer/Package/LinkConstraint/MultiConstraint.php +++ b/src/Composer/Package/LinkConstraint/MultiConstraint.php @@ -13,27 +13,41 @@ namespace Composer\Package\LinkConstraint; /** - * Defines a conjunctive set of constraints on the target of a package link + * Defines a conjunctive or disjunctive set of constraints on the target of a package link * * @author Nils Adermann + * @author Jordi Boggiano */ class MultiConstraint implements LinkConstraintInterface { protected $constraints; protected $prettyString; + protected $conjunctive; /** * Sets operator and version to compare a package with * - * @param array $constraints A conjunctive set of constraints + * @param array $constraints A set of constraints + * @param bool $conjunctive Whether the constraints should be treated as conjunctive or disjunctive */ - public function __construct(array $constraints) + public function __construct(array $constraints, $conjunctive = true) { $this->constraints = $constraints; + $this->conjunctive = $conjunctive; } public function matches(LinkConstraintInterface $provider) { + if (false === $this->conjunctive) { + foreach ($this->constraints as $constraint) { + if ($constraint->matches($provider)) { + return true; + } + } + + return false; + } + foreach ($this->constraints as $constraint) { if (!$constraint->matches($provider)) { return false; @@ -64,6 +78,6 @@ class MultiConstraint implements LinkConstraintInterface $constraints[] = $constraint->__toString(); } - return '['.implode(', ', $constraints).']'; + return '['.implode($this->conjunctive ? ', ' : ' | ', $constraints).']'; } } diff --git a/src/Composer/Package/Version/VersionParser.php b/src/Composer/Package/Version/VersionParser.php index 2440b6b6a..d71115044 100644 --- a/src/Composer/Package/Version/VersionParser.php +++ b/src/Composer/Package/Version/VersionParser.php @@ -221,21 +221,33 @@ class VersionParser $constraints = $match[1]; } - $constraints = preg_split('{\s*,\s*}', trim($constraints)); + $orConstraints = preg_split('{\s*\|\s*}', trim($constraints)); + $orGroups = array(); + foreach ($orConstraints as $constraints) { + $andConstraints = preg_split('{\s*,\s*}', $constraints); + + if (count($andConstraints) > 1) { + $constraintObjects = array(); + foreach ($andConstraints as $constraint) { + $constraintObjects = array_merge($constraintObjects, $this->parseConstraint($constraint)); + } + } else { + $constraintObjects = $this->parseConstraint($andConstraints[0]); + } - if (count($constraints) > 1) { - $constraintObjects = array(); - foreach ($constraints as $constraint) { - $constraintObjects = array_merge($constraintObjects, $this->parseConstraint($constraint)); + if (1 === count($constraintObjects)) { + $constraint = $constraintObjects[0]; + } else { + $constraint = new MultiConstraint($constraintObjects); } - } else { - $constraintObjects = $this->parseConstraint($constraints[0]); + + $orGroups[] = $constraint; } - if (1 === count($constraintObjects)) { - $constraint = $constraintObjects[0]; + if (1 === count($orGroups)) { + $constraint = $orGroups[0]; } else { - $constraint = new MultiConstraint($constraintObjects); + $constraint = new MultiConstraint($orGroups, false); } $constraint->setPrettyString($prettyConstraint); diff --git a/tests/Composer/Test/Fixtures/installer/disjunctive-multi-constraints.test b/tests/Composer/Test/Fixtures/installer/disjunctive-multi-constraints.test new file mode 100644 index 000000000..b274c5de2 --- /dev/null +++ b/tests/Composer/Test/Fixtures/installer/disjunctive-multi-constraints.test @@ -0,0 +1,24 @@ +--TEST-- +Disjunctive multi constraints work +--COMPOSER-- +{ + "repositories": [ + { + "type": "package", + "package": [ + { "name": "foo", "version": "1.1.0" }, + { "name": "foo", "version": "1.0.0" }, + { "name": "bar", "version": "1.1.0", "require": { "foo": "1.0.*" } } + ] + } + ], + "require": { + "bar": "1.*", + "foo": "1.0.*|1.1.*" + } +} +--RUN-- +install +--EXPECT-- +Installing foo (1.0.0) +Installing bar (1.1.0) diff --git a/tests/Composer/Test/Package/Version/VersionParserTest.php b/tests/Composer/Test/Package/Version/VersionParserTest.php index ff1fa6871..e0d90d1cd 100644 --- a/tests/Composer/Test/Package/Version/VersionParserTest.php +++ b/tests/Composer/Test/Package/Version/VersionParserTest.php @@ -284,6 +284,17 @@ class VersionParserTest extends \PHPUnit_Framework_TestCase $this->assertSame((string) $multi, (string) $parser->parseConstraints('>2.0,<=3.0')); } + public function testParseConstraintsMultiDisjunctiveHasPrioOverConjuctive() + { + $parser = new VersionParser; + $first = new VersionConstraint('>', '2.0.0.0'); + $second = new VersionConstraint('<', '2.0.5.0-dev'); + $third = new VersionConstraint('>', '2.0.6.0'); + $multi1 = new MultiConstraint(array($first, $second)); + $multi2 = new MultiConstraint(array($multi1, $third), false); + $this->assertSame((string) $multi2, (string) $parser->parseConstraints('>2.0,<2.0.5 | >2.0.6')); + } + public function testParseConstraintsMultiWithStabilities() { $parser = new VersionParser;