diff --git a/src/Composer/Package/Version/VersionParser.php b/src/Composer/Package/Version/VersionParser.php index 55d93b176..a49a16d9f 100644 --- a/src/Composer/Package/Version/VersionParser.php +++ b/src/Composer/Package/Version/VersionParser.php @@ -22,6 +22,8 @@ use Composer\Package\LinkConstraint\VersionConstraint; */ class VersionParser { + private $modifierRegex = '[.-]?(?:(beta|RC|alpha|patch|pl|p)(?:[.-]?(\d+))?)?([.-]?dev)?'; + /** * Normalizes a version string to be able to perform comparisons on it * @@ -33,24 +35,42 @@ class VersionParser $version = trim($version); // match classical versioning - if (preg_match('{^v?(\d{1,3})(\.\d+)?(\.\d+)?-?((?:beta|RC|alpha)\d*)?(-?dev)?$}i', $version, $matches)) { - return $matches[1] + if (preg_match('{^v?(\d{1,3})(\.\d+)?(\.\d+)?(\.\d+)?'.$this->modifierRegex.'$}i', $version, $matches)) { + $version = $matches[1] .(!empty($matches[2]) ? $matches[2] : '.0') .(!empty($matches[3]) ? $matches[3] : '.0') - .(!empty($matches[4]) ? '-'.strtolower($matches[4]) : '') - .(!empty($matches[5]) ? '-dev' : ''); + .(!empty($matches[4]) ? $matches[4] : '.0'); + $index = 5; + } elseif (preg_match('{^v?(\d{4}(?:[.:-]?\d{2}){1,6}(?:[.:-]?\d{1,3})?)'.$this->modifierRegex.'$}i', $version, $matches)) { // match date-based versioning + $version = preg_replace('{\D}', '-', $matches[1]); + $index = 2; } - // match date-based versioning - if (preg_match('{^v?(\d{4}(?:[.:-]?\d{2}){1,6}(?:[.:-]?\d{1})?)((?:beta|RC|alpha)\d*)?(-?dev)?$}i', $version, $matches)) { - return preg_replace('{\D}', '-', $matches[1]) - .(!empty($matches[2]) ? '-'.strtolower($matches[2]) : '') - .(!empty($matches[3]) ? '-dev' : ''); + // add version modifiers if a version was matched + if (isset($index)) { + if (!empty($matches[$index])) { + $mod = array('p', 'pl', 'rc'); + $modNormalized = array('patch', 'patch', 'RC'); + $version .= '-'.str_replace($mod, $modNormalized, strtolower($matches[$index])) + . (!empty($matches[$index+1]) ? $matches[$index+1] : ''); + } + + if (!empty($matches[$index+2])) { + $version .= '-dev'; + } + + return $version; } throw new \UnexpectedValueException('Invalid version string '.$version); } + /** + * Parses as constraint string into LinkConstraint objects + * + * @param string $constraints + * @return \Composer\Package\LinkConstraint\LinkConstraintInterface + */ public function parseConstraints($constraints) { $constraints = preg_split('{\s*,\s*}', trim($constraints)); @@ -78,12 +98,17 @@ class VersionParser } // match wildcard constraints - if (preg_match('{^(\d+)(?:\.(\d+))?\.\*$}', $constraint, $matches)) { - $lowVersion = $matches[1] . '.' . (isset($matches[2]) ? $matches[2] : '0') . '.0'; - $highVersion = (isset($matches[2]) - ? $matches[1] . '.' . ($matches[2]+1) - : ($matches[1]+1) . '.0') - . '.0'; + if (preg_match('{^(\d+)(?:\.(\d+))?(?:\.(\d+))?\.\*$}', $constraint, $matches)) { + if (isset($matches[3])) { + $lowVersion = $matches[1] . '.' . $matches[2] . '.' . $matches[3] . '.0'; + $highVersion = $matches[1] . '.' . $matches[2] . '.' . ($matches[3]+1) . '.0'; + } elseif (isset($matches[2])) { + $lowVersion = $matches[1] . '.' . $matches[2] . '.0.0'; + $highVersion = $matches[1] . '.' . ($matches[2]+1) . '.0.0'; + } else { + $lowVersion = $matches[1] . '.0.0.0'; + $highVersion = ($matches[1]+1) . '.0.0.0'; + } return array( new VersionConstraint('>=', $lowVersion), diff --git a/src/Composer/Repository/PlatformRepository.php b/src/Composer/Repository/PlatformRepository.php index 64ae9bee4..fa87d7206 100644 --- a/src/Composer/Repository/PlatformRepository.php +++ b/src/Composer/Repository/PlatformRepository.php @@ -53,7 +53,7 @@ class PlatformRepository extends ArrayRepository implements WritableRepositoryIn try { $version = $versionParser->normalize($reflExt->getVersion()); } catch (\UnexpectedValueException $e) { - $version = '0.0.0'; + $version = $versionParser->normalize('0'); } $ext = new MemoryPackage('ext/'.strtolower($ext), $version); diff --git a/tests/Composer/Test/Package/Version/VersionParserTest.php b/tests/Composer/Test/Package/Version/VersionParserTest.php index 25bd99341..64c8616bd 100644 --- a/tests/Composer/Test/Package/Version/VersionParserTest.php +++ b/tests/Composer/Test/Package/Version/VersionParserTest.php @@ -30,19 +30,23 @@ class VersionParserTest extends \PHPUnit_Framework_TestCase public function successfulNormalizedVersions() { return array( - 'none' => array('1.0.0', '1.0.0'), - 'parses state' => array('1.0.0RC1dev', '1.0.0-rc1-dev'), - 'CI parsing' => array('1.0.0-rC15-dev', '1.0.0-rc15-dev'), - 'forces x.y.z' => array('1.0-dev', '1.0.0-dev'), - 'forces x.y.z' => array('0', '0.0.0'), - 'parses long' => array('10.4.13-beta', '10.4.13-beta'), + 'none' => array('1.0.0', '1.0.0.0'), + 'none' => array('1.2.3.4', '1.2.3.4'), + 'parses state' => array('1.0.0RC1dev', '1.0.0.0-RC1-dev'), + 'CI parsing' => array('1.0.0-rC15-dev', '1.0.0.0-RC15-dev'), + 'delimiters' => array('1.0.0.RC.15-dev', '1.0.0.0-RC15-dev'), + 'forces w.x.y.z' => array('1.0-dev', '1.0.0.0-dev'), + 'forces w.x.y.z' => array('0', '0.0.0.0'), + 'parses long' => array('10.4.13-beta', '10.4.13.0-beta'), 'strips leading v' => array('v1.0.0', '1.0.0'), 'strips leading v' => array('v20100102', '20100102'), + 'parses dates y-m' => array('2010.01', '2010-01'), 'parses dates w/ .' => array('2010.01.02', '2010-01-02'), 'parses dates w/ -' => array('2010-01-02', '2010-01-02'), 'parses numbers' => array('2010-01-02.5', '2010-01-02-5'), 'parses datetime' => array('20100102-203040', '20100102-203040'), 'parses dt+number' => array('20100102203040-10', '20100102203040-10'), + 'parses dt+patch' => array('20100102-203040-p1', '20100102-203040-patch1'), ); } @@ -62,7 +66,7 @@ class VersionParserTest extends \PHPUnit_Framework_TestCase 'empty ' => array(''), 'invalid chars' => array('a'), 'invalid type' => array('1.0.0-meh'), - 'too many bits' => array('1.0.0.0'), + 'too many bits' => array('1.0.0.0.0'), ); } @@ -78,15 +82,15 @@ class VersionParserTest extends \PHPUnit_Framework_TestCase public function simpleConstraints() { return array( - 'greater than' => array('>1.0.0', new VersionConstraint('>', '1.0.0')), - 'lesser than' => array('<1.2.3', new VersionConstraint('<', '1.2.3')), - 'less/eq than' => array('<=1.2.3', new VersionConstraint('<=', '1.2.3')), - 'great/eq than' => array('>=1.2.3', new VersionConstraint('>=', '1.2.3')), - 'equals' => array('=1.2.3', new VersionConstraint('=', '1.2.3')), - 'double equals' => array('==1.2.3', new VersionConstraint('=', '1.2.3')), - 'no op means eq' => array('1.2.3', new VersionConstraint('=', '1.2.3')), - 'completes version' => array('=1.0', new VersionConstraint('=', '1.0.0')), - 'accepts spaces' => array('>= 1.2.3', new VersionConstraint('>=', '1.2.3')), + 'greater than' => array('>1.0.0', new VersionConstraint('>', '1.0.0.0')), + 'lesser than' => array('<1.2.3.4', new VersionConstraint('<', '1.2.3.4')), + 'less/eq than' => array('<=1.2.3', new VersionConstraint('<=', '1.2.3.0')), + 'great/eq than' => array('>=1.2.3', new VersionConstraint('>=', '1.2.3.0')), + 'equals' => array('=1.2.3', new VersionConstraint('=', '1.2.3.0')), + 'double equals' => array('==1.2.3', new VersionConstraint('=', '1.2.3.0')), + 'no op means eq' => array('1.2.3', new VersionConstraint('=', '1.2.3.0')), + 'completes version' => array('=1.0', new VersionConstraint('=', '1.0.0.0')), + 'accepts spaces' => array('>= 1.2.3', new VersionConstraint('>=', '1.2.3.0')), ); } @@ -104,14 +108,24 @@ class VersionParserTest extends \PHPUnit_Framework_TestCase public function wildcardConstraints() { return array( - array('2.*', new VersionConstraint('>=', '2.0.0'), new VersionConstraint('<', '3.0.0')), - array('20.*', new VersionConstraint('>=', '20.0.0'), new VersionConstraint('<', '21.0.0')), - array('2.0.*', new VersionConstraint('>=', '2.0.0'), new VersionConstraint('<', '2.1.0')), - array('2.2.*', new VersionConstraint('>=', '2.2.0'), new VersionConstraint('<', '2.3.0')), - array('2.10.*', new VersionConstraint('>=', '2.10.0'), new VersionConstraint('<', '2.11.0')), + array('2.*', new VersionConstraint('>=', '2.0.0.0'), new VersionConstraint('<', '3.0.0.0')), + array('20.*', new VersionConstraint('>=', '20.0.0.0'), new VersionConstraint('<', '21.0.0.0')), + array('2.0.*', new VersionConstraint('>=', '2.0.0.0'), new VersionConstraint('<', '2.1.0.0')), + array('2.2.*', new VersionConstraint('>=', '2.2.0.0'), new VersionConstraint('<', '2.3.0.0')), + array('2.10.*', new VersionConstraint('>=', '2.10.0.0'), new VersionConstraint('<', '2.11.0.0')), + array('2.1.3.*', new VersionConstraint('>=', '2.1.3.0'), new VersionConstraint('<', '2.1.4.0')), ); } + public function testParseConstraintsMulti() + { + $parser = new VersionParser; + $first = new VersionConstraint('>', '2.0.0.0'); + $second = new VersionConstraint('<=', '3.0.0.0'); + $multi = new MultiConstraint(array($first, $second)); + $this->assertEquals((string) $multi, (string) $parser->parseConstraints('>2.0,<=3.0')); + } + /** * @dataProvider failingConstraints * @expectedException UnexpectedValueException diff --git a/tests/Composer/Test/Repository/FilesystemRepositoryTest.php b/tests/Composer/Test/Repository/FilesystemRepositoryTest.php index eda202302..048c9c8d7 100644 --- a/tests/Composer/Test/Repository/FilesystemRepositoryTest.php +++ b/tests/Composer/Test/Repository/FilesystemRepositoryTest.php @@ -49,7 +49,7 @@ class FilesystemRepositoryTest extends \PHPUnit_Framework_TestCase $data = json_decode(file_get_contents($this->repositoryFile), true); $this->assertEquals(array( - array('name' => 'package1', 'type' => 'vendor', 'version' => '1.0.0-beta', 'names' => array('package1')) + array('name' => 'package1', 'type' => 'vendor', 'version' => '1.0.0.0-beta', 'names' => array('package1')) ), $data); } }