-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Ondřej Ešler
committed
Nov 7, 2018
1 parent
8f756e2
commit dbf969a
Showing
4 changed files
with
248 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,6 +1,6 @@ | ||
{ | ||
"name": "intraworlds/enum", | ||
"description": "Lightweight implementation of enum value object in PHP.", | ||
"description": "Lightweight implementation of enum value object", | ||
"type": "library", | ||
"require-dev": { | ||
"phpunit/phpunit": "^7.4" | ||
|
@@ -12,6 +12,11 @@ | |
"email": "[email protected]" | ||
} | ||
], | ||
"autoload": { | ||
"psr-4": { | ||
"IW\\": "src" | ||
} | ||
}, | ||
"require": { | ||
"php": ">=7.1" | ||
} | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
<?xml version="1.0" encoding="UTF-8"?> | ||
<phpunit colors="true"> | ||
<testsuites> | ||
<testsuite> | ||
<directory>tests/</directory> | ||
</testsuite> | ||
</testsuites> | ||
</phpunit> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,155 @@ | ||
<?php declare(strict_types=1); | ||
|
||
namespace IW; | ||
|
||
/** | ||
* Base for enum-like value object, it defines static methods with same names as constants which returns singleton | ||
* of particular enum and its value. | ||
* | ||
* Implementation is simpler version of library https://packagist.org/packages/myclabs/php-enum with added singletons | ||
* to be able use strict equals. | ||
* | ||
* Examples: | ||
* | ||
* final class Hash extends Enum | ||
* { | ||
* const MD5 = 'md5'; | ||
* const SHA1 = 'sha1'; | ||
* } | ||
* | ||
* $a = Hash::MD5(); | ||
* var_dump($a === Hash::MD5()); | ||
* > true | ||
* | ||
* // ALWAYS use strict equal! see the problem | ||
* var_dump($a == Hash::SHA1()); | ||
* > true | ||
* | ||
* var_dump($a->getValue() === Hash::MD5); | ||
* > true | ||
* | ||
* function crack(Hash $hash) { | ||
* echo 'cracking ... ' . $hash; | ||
* } | ||
* | ||
* crack(Hash::SHA1()); | ||
* > cracking ... sha1 | ||
* crack(Hash::SHA1); | ||
* > throws TypeError | ||
* | ||
* switch ($a->getValue()) { | ||
* case Hash::MD5: ... | ||
* case Hash::SHA1: ... | ||
* } | ||
* | ||
* @author Ondrej Esler <[email protected]> | ||
*/ | ||
abstract class Enum | ||
{ | ||
/** @var string */ | ||
private $key; | ||
|
||
/** @var mixed */ | ||
private $_value; | ||
|
||
/** @var array */ | ||
private static $_singletons = []; | ||
|
||
/** | ||
* Constructor | ||
* | ||
* @param string $key | ||
*/ | ||
final private function __construct(string $key) { | ||
if (!defined(static::class . '::' . $key)) { | ||
throw new \InvalidArgumentException('Unknown constant: ' . static::class . '::' . $key); | ||
} | ||
|
||
$this->key = $key; | ||
$this->value = \constant(static::class . '::' . $key); | ||
} | ||
|
||
/** | ||
* Map class constants to static methods const FOO => FOO(), returns singleton of an enum with value of constant | ||
* | ||
* @param string $key | ||
* @param array $arguments | ||
* | ||
* @return Enum | ||
*/ | ||
final public static function __callStatic(string $key, array $arguments): Enum { | ||
$singletonId = static::class . $key; | ||
|
||
if (empty(self::$_singletons[$singletonId])) { | ||
self::$_singletons[$singletonId] = new static($key); | ||
} | ||
|
||
return self::$_singletons[$singletonId]; | ||
} | ||
|
||
/** | ||
* Returns string representation of constant value | ||
* | ||
* @return string | ||
*/ | ||
final public function __toString(): string { | ||
return is_array($this->value) ? json_encode($this->value) : (string) $this->value; | ||
} | ||
|
||
/** | ||
* Return TRUE if given enum is the same, FALSE otherwise | ||
* | ||
* @param Enum $enum | ||
* | ||
* @return bool | ||
*/ | ||
final public function equals(Enum $enum): bool { | ||
return $this === $enum; | ||
} | ||
|
||
/** | ||
* Returns name of constant | ||
* | ||
* @return string | ||
*/ | ||
final public function getKey(): string { | ||
return $this->key; | ||
} | ||
|
||
/** | ||
* Returns value of constant | ||
* | ||
* @return mixed | ||
*/ | ||
final public function getValue() { | ||
return $this->value; | ||
} | ||
|
||
/** | ||
* Returns list of names of constants | ||
* | ||
* @return string[] | ||
*/ | ||
final public static function keys(): array { | ||
return array_keys(static::toArray()); | ||
} | ||
|
||
/** | ||
* Returns list of values of constants | ||
* | ||
* @return mixed[] | ||
*/ | ||
final public static function values(): array { | ||
return array_values(static::toArray()); | ||
} | ||
|
||
/** | ||
* Returns array with all constants eg. [key => value, ...] | ||
* | ||
* @return mixed[] | ||
*/ | ||
final public static function toArray(): array { | ||
return (new \ReflectionClass(static::class))->getConstants(); | ||
} | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,79 @@ | ||
<?php declare(strict_types=1); | ||
|
||
namespace IW; | ||
|
||
use PHPUnit\Framework\TestCase; | ||
|
||
class EnumTest extends TestCase | ||
{ | ||
|
||
function testFailWhenInvalidKey() { | ||
$this->expectException(\InvalidArgumentException::class); | ||
$this->expectExceptionMessage('Unknown constant: IW\IntraWorldsEnum::KIEV'); | ||
IntraWorldsEnum::KIEV(); | ||
} | ||
|
||
function testMethodEquals() { | ||
$city = IntraWorldsEnum::PILSEN(); | ||
|
||
$this->assertTrue($city->equals(IntraWorldsEnum::PILSEN())); | ||
$this->assertTrue(IntraWorldsEnum::PILSEN()->equals($city)); | ||
|
||
$this->assertFalse($city->equals(IntraWorldsEnum::NEW_YORK())); | ||
$this->assertFalse(IntraWorldsEnum::TAMPA()->equals($city)); | ||
} | ||
|
||
function testThatTwoInstancesAreTheSame() { | ||
$this->assertSame(IntraWorldsEnum::TAMPA(), IntraWorldsEnum::TAMPA()); | ||
} | ||
|
||
function testMethodToString() { | ||
$this->assertSame('1', (string) IntraWorldsEnum::PILSEN()); | ||
$this->assertSame('0.33333333333333', (string) IntraWorldsEnum::TAMPA()); | ||
$this->assertSame('{"foo":["bar"]}', (string) IntraWorldsEnum::MUNICH()); | ||
} | ||
|
||
function testMethodGetValue() { | ||
$this->assertSame(true, IntraWorldsEnum::PILSEN()->getValue()); | ||
$this->assertSame(['foo' => ['bar']], IntraWorldsEnum::MUNICH()->getValue()); | ||
$this->assertSame(42, IntraWorldsEnum::NEW_YORK()->getValue()); | ||
$this->assertSame(1 / 3, IntraWorldsEnum::TAMPA()->getValue()); | ||
} | ||
|
||
function testMethodGetKey() { | ||
$this->assertSame('PILSEN', IntraWorldsEnum::PILSEN()->getKey()); | ||
$this->assertSame('MUNICH', IntraWorldsEnum::MUNICH()->getKey()); | ||
$this->assertSame('NEW_YORK', IntraWorldsEnum::NEW_YORK()->getKey()); | ||
$this->assertSame('TAMPA', IntraWorldsEnum::TAMPA()->getKey()); | ||
} | ||
|
||
function testMethodKeys() { | ||
$this->assertSame(['PILSEN', 'MUNICH', 'NEW_YORK', 'TAMPA'], IntraWorldsEnum::keys()); | ||
} | ||
|
||
function testMethodValues() { | ||
$this->assertSame([true, ['foo' => ['bar']], 42, 1 / 3], IntraWorldsEnum::values()); | ||
} | ||
|
||
function testMethodToArray() { | ||
$this->assertSame([ | ||
'PILSEN' => true, | ||
'MUNICH' => ['foo' => ['bar']], | ||
'NEW_YORK' => 42, | ||
'TAMPA' => 1 / 3, | ||
], IntraWorldsEnum::toArray()); | ||
} | ||
} | ||
|
||
/** | ||
* A value object representing testing enum | ||
* | ||
* @author Ondrej Esler <[email protected]> | ||
*/ | ||
class IntraWorldsEnum extends Enum | ||
{ | ||
const PILSEN = true; | ||
const MUNICH = ['foo' => ['bar']]; | ||
const NEW_YORK = 42; | ||
const TAMPA = 1 / 3; | ||
} |