Skip to content

Commit 3f8e1e0

Browse files
committed
Merge pull request #56 from brianjmiller/pr31
Convert Version into a (pseudo) type-safe enum
2 parents af39a2e + 4310d45 commit 3f8e1e0

2 files changed

Lines changed: 160 additions & 21 deletions

File tree

src/Version.php

Lines changed: 132 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -17,19 +17,143 @@
1717

1818
namespace TinCan;
1919

20-
class Version
20+
use InvalidArgumentException;
21+
22+
/**
23+
* TinCan API Version
24+
*
25+
* @internal implemented similar to a type-safe enum
26+
* @package TinCan
27+
*/
28+
final class Version
2129
{
22-
private static $_supported = array(
23-
"1.0.1",
24-
"1.0.0"
25-
//, "0.95"
26-
);
30+
/**#@+
31+
* Value constants
32+
*
33+
* @var string
34+
*/
35+
const V101 = "1.0.1";
36+
const V100 = "1.0.0";
37+
const V095 = "0.95";
38+
/**#@- */
39+
40+
/** @var array string => bool */
41+
private static $supported = [
42+
self::V101 => true,
43+
self::V100 => true,
44+
self::V095 => false
45+
];
46+
47+
/** @var self[] */
48+
private static $instances = [];
49+
50+
/** @var string */
51+
private $value;
52+
53+
/**
54+
* Constructor
55+
*
56+
* @param string $aValue a version value
57+
* @throws InvalidArgumentException when the value is not recognized
58+
*/
59+
private function __construct($aValue) {
60+
if (!isset(static::$supported[$aValue])) {
61+
throw new InvalidArgumentException("Invalid version [$aValue]");
62+
}
63+
$this->value = $aValue;
64+
}
65+
66+
/**
67+
* Does the value match?
68+
*
69+
* @param string $aValue a value to check
70+
* @return bool
71+
*/
72+
public function hasValue($aValue) {
73+
return $this->value === (string) $aValue;
74+
}
75+
76+
/**
77+
* Is the value contained in a list of versions?
78+
*
79+
* @param string[] $aValueList a list of values to check
80+
* @return bool
81+
*/
82+
public function hasAnyValue(array $aValueList) {
83+
return in_array($this->value, $aValueList);
84+
}
85+
86+
/**
87+
* Is this the latest version?
88+
*
89+
* @return bool
90+
*/
91+
public function isLatest() {
92+
return $this->value === static::latest();
93+
}
94+
95+
/**
96+
* Is this a supported version?
97+
*
98+
* @return bool
99+
*/
100+
public function isSupported() {
101+
return static::$supported[$this->value];
102+
}
103+
104+
/**
105+
* Convert the object to a string
106+
*
107+
* @return string
108+
*/
109+
public function __toString() {
110+
return $this->value;
111+
}
112+
113+
/**
114+
* Factory constructor
115+
*
116+
* @example $version = Version::V101();
117+
* @param string $aValue the called method as a version value
118+
* @param array $arguments unused arguments passed to the method
119+
* @return self
120+
*/
121+
public static function __callStatic($aValue, array $arguments = []) {
122+
$aValue = trim(preg_replace("#v(\d)(95|\d)(\d)?#i", '$1.$2.$3', $aValue), ".");
123+
if (!isset(static::$instances[$aValue])) {
124+
static::$instances[$aValue] = new static($aValue);
125+
}
126+
return static::$instances[$aValue];
127+
}
128+
129+
/**
130+
* Convert a string into a Version instance
131+
*
132+
* @param string $aValue a version value
133+
* @return self
134+
*/
135+
public static function fromString($aValue) {
136+
$aValue = str_replace(".", "", $aValue);
137+
return static::{"V$aValue"}();
138+
}
27139

140+
/**
141+
* List all supported versions
142+
*
143+
* @return string[]
144+
*/
28145
public static function supported() {
29-
return self::$_supported;
146+
return array_keys(array_filter(static::$supported, function($supported) {
147+
return $supported === true;
148+
}));
30149
}
31150

151+
/**
152+
* Retrieve the most recent version
153+
*
154+
* @return string
155+
*/
32156
public static function latest() {
33-
return self::$_supported[0];
157+
return array_keys(static::$supported)[0];
34158
}
35159
}

tests/VersionTest.php

Lines changed: 28 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -20,23 +20,38 @@
2020
use TinCan\Version;
2121

2222
class VersionTest extends \PHPUnit_Framework_TestCase {
23+
public function testStaticFactoryReturnsInstance() {
24+
$this->assertInstanceOf("TinCan\Version", Version::v101(), "factory returns instance");
25+
}
26+
27+
public function testToString() {
28+
$this->assertInternalType("string", (string) Version::v101(), "object converts to string");
29+
}
30+
31+
public function testHasValueReturnsBool() {
32+
$this->assertTrue(Version::v101()->hasValue(Version::V101), "object has correct value");
33+
}
34+
35+
public function testHasAnyValueReturnsBool() {
36+
$this->assertFalse(Version::v101()->hasAnyValue([Version::V100, Version::V095]), "object does not have values");
37+
}
38+
39+
public function testIsSupportedReturnsBool() {
40+
$this->assertTrue(Version::v100()->isSupported(), "1.0.0 should be supported");
41+
$this->assertFalse(Version::v095()->isSupported(), "0.95 should not be supported");
42+
}
43+
44+
public function testIsLatestReturnsBool() {
45+
$this->assertTrue(Version::v101()->isLatest(), "1.0.1 should be the latest version");
46+
$this->assertFalse(Version::v095()->isLatest(), "0.95 should not be the latest version");
47+
}
48+
2349
public function testSupported() {
2450
$result = Version::supported();
25-
26-
$this->assertEquals(
27-
[
28-
"1.0.1",
29-
"1.0.0"
30-
//, "0.95"
31-
],
32-
$result,
33-
"match supported"
34-
);
51+
$this->assertNotContains(Version::V095, $result, "0.95 not included");
3552
}
3653

3754
public function testLatest() {
38-
$result = Version::latest();
39-
40-
$this->assertSame("1.0.1", $result, "match latest");
55+
$this->assertSame(Version::V101, Version::latest(), "match latest");
4156
}
4257
}

0 commit comments

Comments
 (0)