From 34a21516c8af4e52033b2fe68f6c5716603fb7ac Mon Sep 17 00:00:00 2001 From: Sam Minnee Date: Wed, 8 May 2013 18:03:29 +1200 Subject: [PATCH] Fix tilde constraints without a stability suffix to work like wildcard constraints. With this fix, a tilde constraint such as ~3.1 won't match unstable versions of 3.1, but a wildcard constraint such as 3.1.* would. This seems like a confusing inconsistency, and so I have corrected it. --- .../Package/Version/VersionParser.php | 80 ++++++++++++++----- .../Package/Version/VersionParserTest.php | 10 ++- 2 files changed, 68 insertions(+), 22 deletions(-) diff --git a/src/Composer/Package/Version/VersionParser.php b/src/Composer/Package/Version/VersionParser.php index ba9b460b0..a6a53fcfb 100644 --- a/src/Composer/Package/Version/VersionParser.php +++ b/src/Composer/Package/Version/VersionParser.php @@ -269,33 +269,49 @@ class VersionParser return array(new EmptyConstraint); } + // match tilde constraints + // like wildcard constraints, unsuffixed tilde constraints say that they must be greater than the previous + // version, to ensure that unstable instances of the current version are allowed. + // however, if a stability suffix is added to the constraint, then a >= match on the current version is + // used instead if (preg_match('{^~(\d+)(?:\.(\d+))?(?:\.(\d+))?(?:\.(\d+))?'.self::$modifierRegex.'?$}i', $constraint, $matches)) { - if (isset($matches[4]) && '' !== $matches[4]) { - $highVersion = $matches[1] . '.' . $matches[2] . '.' . ($matches[3] + 1) . '.0-dev'; - $lowVersion = $matches[1] . '.' . $matches[2] . '.' . $matches[3]. '.' . $matches[4]; - } elseif (isset($matches[3]) && '' !== $matches[3]) { - $highVersion = $matches[1] . '.' . ($matches[2] + 1) . '.0.0-dev'; - $lowVersion = $matches[1] . '.' . $matches[2] . '.' . $matches[3]. '.0'; - } else { - $highVersion = ($matches[1] + 1) . '.0.0.0-dev'; - if (isset($matches[2]) && '' !== $matches[2]) { - $lowVersion = $matches[1] . '.' . $matches[2] . '.0.0'; - } else { - $lowVersion = $matches[1] . '.0.0.0'; - } - } + // Work out which position in the version we are operating at + if (isset($matches[4]) && '' !== $matches[4]) $position = 4; + else if (isset($matches[3]) && '' !== $matches[3]) $position = 3; + else if (isset($matches[2]) && '' !== $matches[2]) $position = 2; + else $position = 1; + + // Calculate the stability suffix + $stabilitySuffix = ''; if (!empty($matches[5])) { - $lowVersion .= '-' . $this->expandStability($matches[5]) . (!empty($matches[6]) ? $matches[6] : ''); + $stabilitySuffix .= '-' . $this->expandStability($matches[5]) . (!empty($matches[6]) ? $matches[6] : ''); } if (!empty($matches[7])) { - $lowVersion .= '-dev'; + $stabilitySuffix .= '-dev'; } + // If we don't have a stability suffix, the lower bound is "> the previous version" + if ($stabilitySuffix == '') { + $lowVersion = $this->manipulateVersionString($matches, $position,-1,'9999999'); + $lowerBound = new VersionConstraint('>', $lowVersion); + + // If we have a stability suffix, then our comparison is ">= this version" + } else { + $lowVersion = $this->manipulateVersionString($matches,$position,0); + $lowerBound = new VersionConstraint('>=', $lowVersion . $stabilitySuffix); + } + + // For upper bound, we increment the position of one more significance, + // but highPosition = 0 would be illegal + $highPosition = max(1,$position-1); + $highVersion = $this->manipulateVersionString($matches,$highPosition,1).'-dev'; + $upperBound = new VersionConstraint('<', $highVersion); + return array( - new VersionConstraint('>=', $lowVersion), - new VersionConstraint('<', $highVersion), + $lowerBound, + $upperBound ); } @@ -355,6 +371,34 @@ class VersionParser throw new \UnexpectedValueException($message); } + /** + * Increment, decrement, or simply pad a version number. + * + * Support function for {@link parseConstraint()} + * + * @param array $matches Array with version parts in array indexes 1,2,3,4 + * @param int $position 1,2,3,4 - which segment of the version to decrement + * @param string $pad The string to pad version parts after $position + * @return string The new version + */ + private function manipulateVersionString($matches, $position, $increment = 0, $pad = '0') { + for($i = 4; $i>0; $i--) { + if($i > $position) { + $matches[$i] = $pad; + + } else if(($i == $position) && $increment) { + $matches[$i] += $increment; + // If $matches[$i] was 0, carry the decrement + if($matches[$i] < 0) { + $matches[$i] = $pad; + $position--; + } + } + } + + return $matches[1] . '.' . $matches[2] . '.' . $matches[3] . '.' . $matches[4]; + } + private function expandStability($stability) { $stability = strtolower($stability); diff --git a/tests/Composer/Test/Package/Version/VersionParserTest.php b/tests/Composer/Test/Package/Version/VersionParserTest.php index c46d47fb9..6d3c1a9de 100644 --- a/tests/Composer/Test/Package/Version/VersionParserTest.php +++ b/tests/Composer/Test/Package/Version/VersionParserTest.php @@ -265,10 +265,12 @@ class VersionParserTest extends \PHPUnit_Framework_TestCase public function tildeConstraints() { return array( - array('~1', new VersionConstraint('>=', '1.0.0.0'), new VersionConstraint('<', '2.0.0.0-dev')), - array('~1.2', new VersionConstraint('>=', '1.2.0.0'), new VersionConstraint('<', '2.0.0.0-dev')), - array('~1.2.3', new VersionConstraint('>=', '1.2.3.0'), new VersionConstraint('<', '1.3.0.0-dev')), - array('~1.2.3.4', new VersionConstraint('>=', '1.2.3.4'), new VersionConstraint('<', '1.2.4.0-dev')), + array('~1', new VersionConstraint('>', '0.9999999.9999999.9999999'), new VersionConstraint('<', '2.0.0.0-dev')), + array('~1.0', new VersionConstraint('>', '0.9999999.9999999.9999999'), new VersionConstraint('<', '2.0.0.0-dev')), + array('~1.0.0', new VersionConstraint('>', '0.9999999.9999999.9999999'), new VersionConstraint('<', '1.1.0.0-dev')), + array('~1.2', new VersionConstraint('>', '1.1.9999999.9999999'), new VersionConstraint('<', '2.0.0.0-dev')), + array('~1.2.3', new VersionConstraint('>', '1.2.2.9999999'), new VersionConstraint('<', '1.3.0.0-dev')), + array('~1.2.3.4', new VersionConstraint('>', '1.2.3.3'), new VersionConstraint('<', '1.2.4.0-dev')), array('~1.2-beta',new VersionConstraint('>=', '1.2.0.0-beta'), new VersionConstraint('<', '2.0.0.0-dev')), array('~1.2-b2', new VersionConstraint('>=', '1.2.0.0-beta2'), new VersionConstraint('<', '2.0.0.0-dev')), array('~1.2-BETA2', new VersionConstraint('>=', '1.2.0.0-beta2'), new VersionConstraint('<', '2.0.0.0-dev')),