Browse Source

Merge remote-tracking branch 'sminnee/fix-tilde-constraint-stabililty'

Jordi Boggiano 11 years ago
parent
commit
533512879e

+ 76 - 39
src/Composer/Package/Version/VersionParser.php

@@ -270,65 +270,71 @@ 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)) {
+
+            // Work out which position in the version we are operating at 
             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];
+                $position = 4;
             } elseif (isset($matches[3]) && '' !== $matches[3]) {
-                $highVersion = $matches[1] . '.' . ($matches[2] + 1) . '.0.0-dev';
-                $lowVersion = $matches[1] . '.' . $matches[2] . '.' . $matches[3]. '.0';
+                $position = 3;
+            } elseif (isset($matches[2]) && '' !== $matches[2]) {
+                $position = 2;
             } 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';
-                }
+                $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(!$stabilitySuffix) $stabilitySuffix = "-dev";
+            $lowVersion = $this->manipulateVersionString($matches, $position, 0) . $stabilitySuffix;
+            $lowerBound = new VersionConstraint('>=', $lowVersion);
+
+            // 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
             );
         }
 
         // match wildcard constraints
         if (preg_match('{^(\d+)(?:\.(\d+))?(?:\.(\d+))?\.[x*]$}', $constraint, $matches)) {
-            if (isset($matches[3])) {
-                $highVersion = $matches[1] . '.' . $matches[2] . '.' . $matches[3] . '.9999999';
-                if ($matches[3] === '0') {
-                    $lowVersion = $matches[1] . '.' . ($matches[2] - 1) . '.9999999.9999999';
-                } else {
-                    $lowVersion = $matches[1] . '.' . $matches[2] . '.' . ($matches[3] - 1). '.9999999';
-                }
-            } elseif (isset($matches[2])) {
-                $highVersion = $matches[1] . '.' . $matches[2] . '.9999999.9999999';
-                if ($matches[2] === '0') {
-                    $lowVersion = ($matches[1] - 1) . '.9999999.9999999.9999999';
-                } else {
-                    $lowVersion = $matches[1] . '.' . ($matches[2] - 1) . '.9999999.9999999';
-                }
+            if (isset($matches[3]) && '' !== $matches[3]) {
+                $position = 3;
+            } elseif (isset($matches[2]) && '' !== $matches[2]) {
+                $position = 2;
             } else {
-                $highVersion = $matches[1] . '.9999999.9999999.9999999';
-                if ($matches[1] === '0') {
-                    return array(new VersionConstraint('<', $highVersion));
-                } else {
-                    $lowVersion = ($matches[1] - 1) . '.9999999.9999999.9999999';
-                }
+                $position = 1;
             }
 
-            return array(
-                new VersionConstraint('>', $lowVersion),
-                new VersionConstraint('<', $highVersion),
-            );
+            $lowVersion = $this->manipulateVersionString($matches, $position) . "-dev";
+            $highVersion = $this->manipulateVersionString($matches, $position, 1) . "-dev";
+
+            if($lowVersion === "0.0.0.0-dev") {
+                return array(new VersionConstraint('<', $highVersion));
+            } else {
+                return array(
+                    new VersionConstraint('>=', $lowVersion),
+                    new VersionConstraint('<', $highVersion),
+                );
+            }
         }
 
         // match operators constraints
@@ -350,12 +356,43 @@ class VersionParser
 
         $message = 'Could not parse version constraint '.$constraint;
         if (isset($e)) {
-            $message .= ': '.$e->getMessage();
+            $message .= ': '. $e->getMessage();
         }
 
         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 null on a carry overflow
+                    if($i == 1) return null;
+                }
+            }
+        }
+
+        return $matches[1] . '.' . $matches[2] . '.' . $matches[3] . '.' . $matches[4];
+    }
+
     private function expandStability($stability)
     {
         $stability = strtolower($stability);

+ 13 - 11
tests/Composer/Test/Package/Version/VersionParserTest.php

@@ -237,13 +237,13 @@ class VersionParserTest extends \PHPUnit_Framework_TestCase
     public function wildcardConstraints()
     {
         return array(
-            array('2.*',     new VersionConstraint('>', '1.9999999.9999999.9999999'), new VersionConstraint('<', '2.9999999.9999999.9999999')),
-            array('20.*',    new VersionConstraint('>', '19.9999999.9999999.9999999'), new VersionConstraint('<', '20.9999999.9999999.9999999')),
-            array('2.0.*',   new VersionConstraint('>', '1.9999999.9999999.9999999'), new VersionConstraint('<', '2.0.9999999.9999999')),
-            array('2.2.x',   new VersionConstraint('>', '2.1.9999999.9999999'), new VersionConstraint('<', '2.2.9999999.9999999')),
-            array('2.10.x',  new VersionConstraint('>', '2.9.9999999.9999999'), new VersionConstraint('<', '2.10.9999999.9999999')),
-            array('2.1.3.*', new VersionConstraint('>', '2.1.2.9999999'), new VersionConstraint('<', '2.1.3.9999999')),
-            array('0.*',     null, new VersionConstraint('<', '0.9999999.9999999.9999999')),
+            array('2.*',     new VersionConstraint('>=', '2.0.0.0-dev'), new VersionConstraint('<', '3.0.0.0-dev')),
+            array('20.*',    new VersionConstraint('>=', '20.0.0.0-dev'), new VersionConstraint('<', '21.0.0.0-dev')),
+            array('2.0.*',   new VersionConstraint('>=', '2.0.0.0-dev'), new VersionConstraint('<', '2.1.0.0-dev')),
+            array('2.2.x',   new VersionConstraint('>=', '2.2.0.0-dev'), new VersionConstraint('<', '2.3.0.0-dev')),
+            array('2.10.x',  new VersionConstraint('>=', '2.10.0.0-dev'), new VersionConstraint('<', '2.11.0.0-dev')),
+            array('2.1.3.*', new VersionConstraint('>=', '2.1.3.0-dev'), new VersionConstraint('<', '2.1.4.0-dev')),
+            array('0.*',     null, new VersionConstraint('<', '1.0.0.0-dev')),
         );
     }
 
@@ -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('>=', '1.0.0.0-dev'), new VersionConstraint('<', '2.0.0.0-dev')),
+            array('~1.0',     new VersionConstraint('>=', '1.0.0.0-dev'), new VersionConstraint('<', '2.0.0.0-dev')),
+            array('~1.0.0',     new VersionConstraint('>=', '1.0.0.0-dev'), new VersionConstraint('<', '1.1.0.0-dev')),
+            array('~1.2',     new VersionConstraint('>=', '1.2.0.0-dev'), new VersionConstraint('<', '2.0.0.0-dev')),
+            array('~1.2.3',   new VersionConstraint('>=', '1.2.3.0-dev'), new VersionConstraint('<', '1.3.0.0-dev')),
+            array('~1.2.3.4', new VersionConstraint('>=', '1.2.3.4-dev'), 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')),