Skip to content

Commit

Permalink
version 2
Browse files Browse the repository at this point in the history
  • Loading branch information
amouhzi committed Oct 20, 2014
1 parent ae83c73 commit e643785
Show file tree
Hide file tree
Showing 19 changed files with 1,476 additions and 699 deletions.
46 changes: 20 additions & 26 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,26 +1,20 @@
Version Helper for PHP projects
===============================

This module normalize versions as Composer do. Parse constraints as Composer do too.
And tests if a version match a constraint.

Example:

```php
<?php

$parser = new \Version\VersionParser();

echo $parser->parseStability('1.2-RC2'); // RC
echo $parser->parseStability('2.0b'); // beta
echo $parser->parseConstraints('1.0'); // stable

echo $parser->normalize('2.0b1'); // 2.0.0.0-beta1

$c = $parser->parseConstraints('>=1.2.5,<2.0');
echo $c->match('1.2.0'); // false
echo $c->match('1.5'); // true
echo $c->match('2.0'); // false
?>
```
Versions and Constraints for PHP
================================

This library parse versions,
E.x.:
<code>1.0.0</code>
<code>1.0.2-stable</code>
<code>1.0.20-alpha2</code>.
It can parse constraints (like Composer versions),
E.x.:
<code>>=1.0 >=1.0,<2.0 >=1.0,<1.1 | >=1.2</code>,
<code>1.0.*</code>,
<code>~1.2</code>.

The goal of that is to let you check if a version matches a constraint,
or to check if a constraint is a subset of another constraint.

All that is done to let us select which version is compatible with a user constraints.

It works with the same rules of Composer versioning.
184 changes: 184 additions & 0 deletions src/Version/Constraint.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,184 @@
<?php

namespace Version;

use Version\Constraint\AnythingConstraint;
use Version\Constraint\MultiConstraint;
use Version\Constraint\SimpleConstraint;

abstract class Constraint
{
public abstract function matches(Constraint $constraint);

/**
* @param string $input
* @return Constraint
*/
public static function parse($input)
{
$input = trim($input);

if (strlen($input) == 0) {
throw new \UnexpectedValueException('Empty.');
}

$input = explode(',', $input);
if (count($input) > 1) {
$and = true;
} else {
$input = explode('|', $input[0]);
$and = false;
}

if (count($input) > 1) {
$constraints = array();
foreach ($input as $constraint) {
$constraints[] = self::parse($constraint);
}
return new MultiConstraint($constraints, $and);
}

$input = $input[0];

$regex = '/^' .
'(?:([\*|x])\.)?' .
'(?:([\*|x])\.)?' .
'(?:([\*|x])\.)?' .
'(?:([\*|x]))?' .
'$/';

if (preg_match($regex, $input, $matches)) {
return new AnythingConstraint();
}

$regex = '/^' .
'(?:(' . Operator::REGEX . '))? *' .
'(?:(\d+|\*|x)\.)?' .
'(?:(\d+|\*|x)\.)?' .
'(?:(\d+|\*|x)\.)?' .
'(?:(\d+|\*|x))?' .
'(?:' . Stability::REGEX . ')?' .
'$/';

if(!preg_match($regex, $input, $matches)) {
throw new \UnexpectedValueException('Invalid type: ' . $input);
}

if (isset($matches[1]) && strlen($matches[1]) > 0) {
$operator = $matches[1];
} else {
$operator = '=';
}
$operator = new Operator($operator);

$parts = array();

if (isset($matches[2]) && strlen($matches[2]) > 0) $parts[] = $matches[2];
if (isset($matches[3]) && strlen($matches[3]) > 0) $parts[] = $matches[3];
if (isset($matches[4]) && strlen($matches[4]) > 0) $parts[] = $matches[4];
if (isset($matches[5]) && strlen($matches[5]) > 0) $parts[] = $matches[5];

if ((string)$operator == '~') {
$end = count($parts);
} else {
$end = null;
}

while (count($parts) < 4) {
$parts[] = 0;
}

$max = $parts;

if ($end) {
if ($end == 1) {
$max[0]++;
} elseif ($end == 2) {
$max[0]++;
$max[1] = 0;
} elseif ($end == 3) {
$max[1]++;
$max[2] = 0;
} elseif ($end == 4) {
$max[2]++;
$max[3] = 0;
} else {
echo $end;
die($end);
}
}

if ($parts[3] === 'x' || $parts[3] === '*') {
$parts[3] = 0;
$max[3] = 0;
$max[2]++;
}

if ($parts[2] === 'x' || $parts[2] === '*') {
$parts[2] = 0;
$max[2] = 0;
$max[1]++;
}

if ($parts[1] === 'x' || $parts[1] === '*') {
$parts[1] = 0;
$max[1] = 0;
$max[0]++;
}

$version = new Version($parts[0]);
if (isset($parts[1])) $version->setMinor($parts[1]);
if (isset($parts[2])) $version->setRevision($parts[2]);
if (isset($parts[3])) $version->setMicro($parts[3]);

if (isset($matches[6]) && strlen($matches[6]) > 0) {
if (strtolower($matches[5]) == 'rc') {
$stability = 'RC';
} elseif (in_array(strtolower($matches[6]), array('pl', 'patch', 'p'))) {
$stability = 'patch';
} elseif (in_array(strtolower($matches[6]), array('beta', 'b'))) {
$stability = 'beta';
} elseif (strtolower($matches[6]) == 'stable') {
$stability = 'stable';
} else {
throw new \UnexpectedValueException('Invalid type: ' . $input);
}
$version->setStability(new Stability($stability, $matches[7]));
}

foreach ($parts as $k => $v) {
if ($v != $max[$k]) {
if ($input == '<=1.2.3') {
print_r($parts);
print_r($max);
print_r(array_diff($parts, $max));
die;
}
$maxVersion = new Version($max[0]);
if (isset($max[1])) $maxVersion->setMinor($max[1]);
if (isset($max[2])) $maxVersion->setRevision($max[2]);
if (isset($max[3])) $maxVersion->setMicro($max[3]);

if ((string)$version == '0.0.0.0') {
return new SimpleConstraint(new Operator('<'), $maxVersion);
}
if (isset($matches[6]) && strtolower($matches[6]) == 'stable') {
$version->setStability(new Stability());
}
return new MultiConstraint(array(
new SimpleConstraint(new Operator('>='), $version),
new SimpleConstraint(new Operator('<'), $maxVersion)
));
}
}

return new SimpleConstraint($operator, $version);
}

abstract public function isSubsetOf(Constraint $constraint);

public function isIncluding(Constraint $constraint)
{
return $constraint->isSubsetOf($this);
}
}
8 changes: 0 additions & 8 deletions src/Version/Constraint/AbstractConstraint.php

This file was deleted.

24 changes: 24 additions & 0 deletions src/Version/Constraint/AnythingConstraint.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
<?php

namespace Version\Constraint;

use Version\Constraint;

class AnythingConstraint extends Constraint
{
public function __toString()
{
return '*';
}

public function matches(Constraint $constraint)
{
return true;
}

public function isSubsetOf(Constraint $constraint)
{
throw new \Exception('Constraint comparison of * with constraint ' .
$constraint . ' Not implemented yet');
}
}
16 changes: 0 additions & 16 deletions src/Version/Constraint/EmptyConstraint.php

This file was deleted.

77 changes: 67 additions & 10 deletions src/Version/Constraint/MultiConstraint.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,33 +2,90 @@

namespace Version\Constraint;

class MultiConstraint
use Version\Constraint;

class MultiConstraint extends Constraint
{
public function __construct(array $minMax, $and = true)
/**
* @var Constraint[]
*/
private $constraints;

public function __construct(array $constraints, $and = true)
{
$this->minMax = $minMax;
$this->constraints = $constraints;
$this->and = $and;
}

public function __toString()
{
return implode($this->and ? ',' : '|', $this->minMax);
return implode($this->and ? ',' : '|', $this->constraints);
}

public function match($version)
public function matches(Constraint $constraint)
{
if($this->and) {
foreach($this->minMax as $c) {
if(!$c->match($version))
if ($this->and) {
foreach ($this->constraints as $c) {
if (!$c->matches($constraint))
return false;
}
return true;
} else {
foreach($this->minMax as $c) {
if($c->match($version))
foreach ($this->constraints as $c) {
if ($c->matches($constraint))
return true;
}
return false;
}
}

public function isSubsetOf(Constraint $constraint)
{
if($constraint instanceof SimpleConstraint) {
foreach ($this->constraints as $child) {
if ($child->isSubsetOf($constraint)) {
return true;
}
}
return false;
}
if($constraint instanceof MultiConstraint) {
if(count($this->constraints) == 2) {
if(count($constraint->constraints) == 2) {
$min1 = $this->constraints[0];
$max1 = $this->constraints[1];
$min2 = $constraint->constraints[0];
$max2 = $constraint->constraints[1];
if(
$min1 instanceof SimpleConstraint &&
$min2 instanceof SimpleConstraint &&
$max1 instanceof SimpleConstraint &&
$max2 instanceof SimpleConstraint
){
if(
in_array((string) $min1->getOperator() , array('>', '>=')) &&
in_array((string) $min2->getOperator() , array('>', '>=')) &&
in_array((string) $max1->getOperator() , array('<', '<=')) &&
in_array((string) $max2->getOperator() , array('<', '<='))
) {
return
$min1->isSubsetOf($min2) &&
$max1->isSubsetOf($max2);
}
}
}
}
}
throw new \Exception('Constraint comparison by ' .
$this . ' with constraint ' . $constraint .
' Not implemented yet');
}

/**
* @return Constraint[]
*/
public function getConstraints()
{
return $this->constraints;
}
}
Loading

0 comments on commit e643785

Please sign in to comment.