Permalink
Browse files

Initial code commit, implemented version and dependency string parsing.

  • Loading branch information...
1 parent e8fbe1c commit a7a71e4e43aab80aae0a9fe0c25416c67971251d @lox lox committed May 15, 2011
Showing with 250 additions and 16 deletions.
  1. +9 −7 README.md
  2. +55 −0 lib/Phark/Dependancy.php
  3. +7 −0 lib/Phark/Exception.php
  4. +91 −0 lib/Phark/Version.php
  5. +5 −3 plan.txt
  6. +1 −4 tests/all.php
  7. +25 −2 tests/deps.php
  8. +57 −0 tests/versions.php
View
@@ -16,7 +16,8 @@ $ curl http://pharkphp.org/install | sh
Using Phark
-----------
-Phark packages can be installed manually with the command-line tool, [phark][].
+Phark packages can be installed manually with the command-line tool,
+[phark](https://github.com/lox/phark/wiki/Using-the-commandline-tools).
```bash
$ cd projects/myproject
@@ -42,10 +43,10 @@ level of the project:
<?php
Phark::deps()
- ->source( 'http://pharkphp.org' )
- ->depends( 'pheasant' )
- ->depends( 'yaml', '>= 1.0' )
- ->depends( 'simpletest', '~> 2.x.beta' )
+ ->source( 'http://pharkphp.org' )
+ ->package( 'pheasant', array('git'=>'https://lox@github.com/lox/pheasant.git'))
+ ->package( 'yaml', '~>1.0.0' )
+ ->package( 'simpletest', '2.0.0beta1', array('group'=>'dev'))
;
```
Then the following should be executed at the top level of the project:
@@ -85,7 +86,7 @@ Check out the wiki page on the [anatomy of a phark package](https://github.com/l
Why the name?
--------------
-Phark was born out of my frustration with trying to write an distribute
+Phark was born out of my frustration with trying to write and distribute
re-usable code in PHP. Phark packages use the PHP Archive format,
[Phar](http://www.php.net/manual/en/book.phar.php).
@@ -98,7 +99,8 @@ Why not PEAR?
PHP5.3 and bulky codebase, PEAR still doesn't allow for multiple versions of a package to be
installed side-by-side, or for per-project installations.
-What's worse is how hard it is to contribute a PEAR package. Phark allows anyone to submit packages for instant consumption by other developers. If you really want you can even consume PEAR packages via this mechanism.
+What's worse is how hard it is to contribute a PEAR package. Phark allows anyone to submit packages for
+instant consumption by other developers. If you really want you can even consume PEAR packages via this mechanism.
References and Reading
----------------------
@@ -0,0 +1,55 @@
+<?php
+
+namespace Phark;
+
+/**
+ * A dependancy on a specific package
+ */
+class Dependancy
+{
+ public $package;
+ private $_constraints=array();
+
+ public function __construct($package, $constraints=array())
+ {
+ $this->package = $package;
+
+ foreach($constraints as $c)
+ $this->_constraints []= $this->_parseConstraint($c);
+ }
+
+ private function _parseConstraint($constraint)
+ {
+ if(!preg_match('/^(~>|<=?|>=?|=|)(\d+\.\d+.\w+)$/',$constraint,$m))
+ throw new Exception("Invalid constraint $constraint");
+
+ $c = new \stdClass();
+ $c->version = new Version($m[2]);
+ $c->operator = $m[1];
+
+ return $c;
+ }
+
+ public function meets($package, $version)
+ {
+ if($package != $this->package)
+ return false;
+
+ if(is_string($version)) $version = new Version($version);
+
+ foreach($this->_constraints as $c)
+ {
+ switch($c->operator)
+ {
+ case '<=': if(!$version->lessOrEqual($c->version)) return false; break;
+ case '<': if(!$version->less($c->version)) return false; break;
+ case '>=': if(!$version->greaterOrEqual($c->version)) return false; break;
+ case '>': if(!$version->greater($c->version)) return false; break;
+ case '=': if(!$version->equal($c->version)) return false; break;
+ case '~>': if(!$version->fuzzyEqual($c->version)) return false; break;
+ }
+ }
+
+ return true;
+ }
+}
@@ -0,0 +1,7 @@
+<?php
+
+namespace Phark;
+
+class Exception extends \Exception
+{
+}
View
@@ -0,0 +1,91 @@
+<?php
+
+namespace Phark;
+
+/**
+ * An implementation of Semantic Version parsing
+ * @see http://semver.org/
+ */
+class Version
+{
+ public $major, $minor, $patch, $special;
+
+ public function __construct($string)
+ {
+ foreach($this->_parse($string) as $key=>$value)
+ if(!is_numeric($key)) $this->$key = $value;
+ }
+
+ private function _parse($string)
+ {
+ if(!preg_match('/^(?<major>\d+)\.(?<minor>\d+).(?:(?<patch>\d+)(?<special>\w+)?)?$/', $string, $m))
+ throw new Exception("Unable to parse version $string");
+
+ return $m;
+ }
+
+ public function __toString()
+ {
+ return sprintf('%d.%d.%d%s', $this->major, $this->minor, $this->patch, $this->special);
+ }
+
+ /**
+ * Returns 1 if this version is greater, 0 if the same, -1 if less
+ * than
+ */
+ public function compare($v)
+ {
+ $v = is_string($v) ? new self($v) : $v;
+ $cmp = strcmp(
+ sprintf('%d.%d.%d', $this->major, $this->minor, $this->patch),
+ sprintf('%d.%d.%d', $v->major, $v->minor, $v->patch)
+ );
+
+ if($cmp == 0)
+ {
+ if(empty($this->special) && !empty($v->special))
+ $cmp = 1;
+ else if(!empty($this->special) && empty($v->special))
+ $cmp = -1;
+ else
+ $cmp = strcmp($this->special, $v->special);
+ }
+
+ return $cmp;
+ }
+
+ public function greater($v) { return $this->compare($v) > 0; }
+ public function greaterOrEqual($v) { return $this->compare($v) >= 0; }
+ public function less($v) { return $this->compare($v) < 0; }
+ public function lessOrEqual($v) { return $this->compare($v) <= 0; }
+ public function equal($v) { return $this->compare($v) == 0; }
+
+ /**
+ * Checks if a version is equal, ignoring the patch number and special code
+ */
+ public function fuzzyEqual($v)
+ {
+ $clone = clone $this;
+ $v = clone $v;
+ $v->patch = $clone->patch = 0;
+ $v->special = $clone->special = NULL;
+
+ return $clone->equal($v);
+ }
+
+ /**
+ * Sorts a list of versions, latest first
+ */
+ public static function sort($versions)
+ {
+ $versions = array_map(function($v) {
+ return is_string($v) ? new Version($v) : $v;
+ },$versions);
+
+ usort($versions, function($a,$b) {
+ return $a->compare($b) * -1;
+ });
+
+ return $versions;
+ }
+}
View
@@ -24,16 +24,18 @@ Phark\Environment // the basic phark environment
Phark\Dependancies // a list of dependancies
Phark\Packages // a directory of packages available
- - all() returns global and local packages
- - local() returns local packages
- - global() returns global packages
+ - all() returns installed global and local packages
+ - local() returns installed local packages
+ - global() returns installed global packages
- spec($package) returns a package specification
- package($package) returns a Package object
Phark\Package // an actual package in the filesystem
- spec() returns the PackageSpec
- uninstall()
+Phark\PackageSource // a collection of packages
+
Phark\PackageSpec // the package specification
- deps() // returns a Dependancies object
View
@@ -1,18 +1,15 @@
<?php
-// simpletest dependancy
require_once __DIR__.'/../vendor/simpletest/autorun.php';
class AllTests extends TestSuite
{
function __construct()
{
- parent::__construct('All tests');
-
$exclude = array('all.php','base.php');
// add all tests
- foreach(glob(dirname(__FILE__).'/*.php') as $file)
+ foreach(glob(__DIR__.'/*.php') as $file)
{
if(!in_array(basename($file), $exclude))
$this->addFile($file);
View
@@ -5,9 +5,32 @@
class DependanciesTest extends \Phark\Tests\TestCase
{
- public function testHelloWorld()
+ public function testParsingExactDependancyStrings()
{
- $this->assertTrue(false, "Hello World");
+ $d1 = new \Phark\Dependancy('package');
+
+ $this->assertFalse($d1->meets('blargh','2.0.0'));
+ $this->assertTrue($d1->meets('package','1.0.0'));
+ }
+
+ public function testRangeDependancyStrings()
+ {
+ $d1 = new \Phark\Dependancy('package', array('>=1.0.0', '<2.0.0'));
+
+ $this->assertTrue($d1->meets('package','1.0.0'));
+ $this->assertTrue($d1->meets('package','1.5.3'));
+ $this->assertFalse($d1->meets('package','2.0.0'));
+ $this->assertFalse($d1->meets('package','5.0.0'));
+ }
+
+ public function testFuzzyMinorVersionDependancyStrings()
+ {
+ $d1 = new \Phark\Dependancy('package', array('~>1.0.0'));
+
+ $this->assertTrue($d1->meets('package','1.0.0'));
+ $this->assertTrue($d1->meets('package','1.0.5'));
+ $this->assertFalse($d1->meets('package','1.2.0'));
+ $this->assertFalse($d1->meets('package','5.0.0'));
}
}
View
@@ -0,0 +1,57 @@
+<?php
+
+require_once __DIR__.'/../vendor/simpletest/autorun.php';
+require_once __DIR__.'/base.php';
+
+class VersionsTest extends \Phark\Tests\TestCase
+{
+ public function testParsingVersions()
+ {
+ $v = new \Phark\Version("1.2.3");
+
+ $this->assertEqual($v->major, 1);
+ $this->assertEqual($v->minor, 2);
+ $this->assertEqual($v->patch, 3);
+ $this->assertEqual((string)$v, "1.2.3");
+
+ $v = new \Phark\Version("1.0.2beta1");
+
+ $this->assertEqual($v->major, 1);
+ $this->assertEqual($v->minor, 0);
+ $this->assertEqual($v->patch, 2);
+ $this->assertEqual($v->special, 'beta1');
+ $this->assertEqual((string)$v, "1.0.2beta1");
+ }
+
+ public function testComparingVersions()
+ {
+ $v1 = new \Phark\Version("1.3.3");
+ $v2 = new \Phark\Version("1.2.5");
+
+ $this->assertTrue($v1->greater($v2));
+ $this->assertTrue($v1->greaterOrEqual($v2));
+ $this->assertTrue($v2->less($v1));
+ $this->assertTrue($v2->lessOrEqual($v1));
+ $this->assertFalse($v2->equal($v1));
+
+ $v1 = new \Phark\Version("1.2.0");
+ $v2 = new \Phark\Version("1.2.0beta1");
+ $v3 = new \Phark\Version("1.2.0beta2");
+ $v4 = new \Phark\Version("1.2.0alpha1");
+
+ $this->assertTrue($v2->less($v1));
+ $this->assertTrue($v3->less($v1));
+ $this->assertTrue($v4->less($v1));
+ }
+
+ public function testSortingVersions()
+ {
+ $versions = array("1.3.1","0.1.0","1.3.1beta1","1.3.1alpha","1.3.1rc1");
+ $sorted = array_map(function($v){ return (string)$v; }, \Phark\Version::sort($versions));
+
+ $this->assertEqual($sorted, array(
+ '1.3.1','1.3.1rc1','1.3.1beta1','1.3.1alpha','0.1.0'
+ ));
+ }
+}
+

0 comments on commit a7a71e4

Please sign in to comment.