Skip to content

Commit

Permalink
3.0.0
Browse files Browse the repository at this point in the history
  • Loading branch information
Shira-3749 committed Jan 5, 2019
1 parent dc65167 commit e5b674a
Show file tree
Hide file tree
Showing 4 changed files with 127 additions and 25 deletions.
11 changes: 11 additions & 0 deletions CHANGELOG.rst
Original file line number Diff line number Diff line change
@@ -1,6 +1,17 @@
Changelog
#########

3.0.0
*****

- enum instances can no longer be cloned
- ``Enum::determineKeyToValueMap()`` is now protected again so it can be overriden
- ``Enum::findValueByKey()`` and ``Enum::findKeyByValue()`` return ``NULL`` on failure
instead of throwing exceptions
- added ``Enum::getValueByKey()`` and ``Enum::getKeyByValue()`` which throw exceptions
on invalid key/value


2.0.1
*****

Expand Down
54 changes: 42 additions & 12 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,29 @@ Defining an enum class
Private and protected constants are ignored.


Custom key-value source
-----------------------

To define key-value pairs using some other source than class constants, override the static
``determineKeyToValueMap()`` method:

.. code:: php
<?php
class Example extends Enum
{
protected static function determineKeyToValueMap(): array
{
return [
'FOO' => 'bar',
'BAZ' => 'qux',
'QUUX' => 'quuz',
];
}
}
Supported value types
=====================

Expand Down Expand Up @@ -117,8 +140,8 @@ Keys and values can be looked up using their counterpart:
<?php
var_dump(
DayOfTheWeek::findValueByKey('FRIDAY'),
DayOfTheWeek::findKeyByValue(4)
DayOfTheWeek::getValueByKey('FRIDAY'),
DayOfTheWeek::getKeyByValue(4)
);
Output:
Expand All @@ -128,6 +151,13 @@ Output:
int(4)
string(6) "FRIDAY"

.. NOTE::

If the key or value doesn't exist, an exception will be thrown. See `Error handling`_.

To get ``NULL`` instead of an exception, use the ``findValueByKey()`` or ``findKeyByValue()``
method instead.


Getting key/value lists and maps
---------------------------------
Expand Down Expand Up @@ -217,13 +247,13 @@ Output:
Creating enum instances
=======================

.. NOTE::
Instances created by ``fromValue()``, ``fromKey()`` and the magic factory methods
are cached internally and reused.

Instances created by ``fromValue()``, ``fromKey()`` and the static magic factory
methods are cached internally and reused.
Multiple calls to the factory methods with the same value or key will yield
the same instance.

Multiple calls to the factory methods with the same value or key will yield
the same instance.
Enum instances cannot be cloned.


Using a value
Expand Down Expand Up @@ -407,7 +437,7 @@ Output:
Error handling
==============

All errors are handled by throwing an exception.
Most error states are handled by throwing an exception.

All exceptions thrown by the ``Enum`` class implement ``Kuria\Enum\Exception\ExceptionInterface``.

Expand All @@ -423,7 +453,7 @@ Invalid value
// or
DayOfTheWeek::findKeyByValue(123456);
DayOfTheWeek::getKeyByValue(123456);
Result:

Expand All @@ -443,7 +473,7 @@ Invalid key
// or
DayOfTheWeek::findValueByKey('NONEXISTENT');
DayOfTheWeek::getValueByKey('NONEXISTENT');
Result:

Expand All @@ -467,7 +497,7 @@ Duplicate values
const BAR = 'foo';
}
EnumWithDuplicateValues::findKeyByValue('foo');
EnumWithDuplicateValues::getKeyByValue('foo');
Result:

Expand Down Expand Up @@ -500,7 +530,7 @@ the following values are equal:

.. NOTE::

The public API, e.g. ``Enum::findValueByKey()`` and ``$enum->value()``,
The public API, e.g. ``Enum::getValueByKey()`` and ``$enum->value()``,
always returns the value as defined by the enum class.

.. NOTE::
Expand Down
52 changes: 41 additions & 11 deletions src/Enum.php
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,15 @@ protected function __construct(string $key, $value)
$this->value = $value;
}

/**
* Enum instance must not be cloned to ensure a single instance per key-value pair
*
* @codeCoverageIgnore
*/
private function __clone()
{
}

function __toString(): string
{
return (string) $this->value;
Expand Down Expand Up @@ -112,7 +121,7 @@ static function __callStatic(string $name, array $arguments)
/**
* Get instance for the given key
*
* @throws InvalidKeyException
* @throws InvalidKeyException if there is no such key
* @return static
*/
static function fromKey(string $key)
Expand All @@ -126,11 +135,12 @@ static function fromKey(string $key)
/**
* Get instance for the given value
*
* @throws InvalidValueException if there is no such value
* @return static
*/
static function fromValue($value)
{
$key = self::findKeyByValue($value);
$key = self::getKeyByValue($value);

return self::$instanceCache[static::class][$key]
?? (self::$instanceCache[static::class][$key] = new static($key, self::$keyToValueMap[static::class][$key]));
Expand All @@ -154,25 +164,45 @@ static function hasValue($value): bool
}

/**
* @throws InvalidKeyException
* @throws InvalidKeyException if there is no such key
*/
static function findValueByKey(string $key)
static function getValueByKey(string $key)
{
self::ensureKeyExists($key);

return self::$keyToValueMap[static::class][$key];
}

/**
* @throws InvalidValueException
* Attempt to find a value by its key. Returns NULL on failure.
*/
static function findValueByKey(string $key)
{
return self::hasKey($key)
? self::$keyToValueMap[static::class][$key]
: null;
}

/**
* @throws InvalidValueException if there is no such value
*/
static function findKeyByValue($value): string
static function getKeyByValue($value): string
{
self::ensureValueExists($value);

return self::$valueToKeyMap[static::class][$value];
}

/**
* Attempt to find a key by its value. Returns NULL on failure.
*/
static function findKeyByValue($value): ?string
{
return self::hasValue($value)
? self::$valueToKeyMap[static::class][$value]
: null;
}

/**
* @return string[]
*/
Expand Down Expand Up @@ -241,7 +271,7 @@ function equals($value): bool
}

/**
* @throws InvalidKeyException if the key does not exist
* @throws InvalidKeyException if there is no such key
*/
static function ensureKeyExists(string $key)
{
Expand All @@ -256,7 +286,7 @@ static function ensureKeyExists(string $key)
}

/**
* @throws InvalidValueException if the value does not exist
* @throws InvalidValueException if there is no such value
*/
static function ensureValueExists($value)
{
Expand Down Expand Up @@ -297,7 +327,7 @@ private static function loadKeyMap()

private static function loadKeyToValueMap()
{
self::$keyToValueMap[static::class] = self::determineKeyToValueMap();
self::$keyToValueMap[static::class] = static::determineKeyToValueMap();
}

private static function loadValueToKeyMap()
Expand Down Expand Up @@ -341,9 +371,9 @@ private static function loadValueToKeyMap()
*
* @return array
*/
private static function determineKeyToValueMap(): array
protected static function determineKeyToValueMap(): array
{
// use all public constants of current class
// default behavior is to use all public constants of the current class
$keyToValueMap = [];

foreach ((new \ReflectionClass(static::class))->getReflectionConstants() as $constant) {
Expand Down
35 changes: 33 additions & 2 deletions tests/EnumTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -44,8 +44,12 @@ function testShouldPerformStaticOperations(string $key, $value)
$this->assertFalse(TestEnum::hasKey('__NONEXISTENT_KEY__'));
$this->assertTrue(TestEnum::hasValue($value));
$this->assertFalse(TestEnum::hasValue('__NONEXISTENT_VALUE__'));
$this->assertSame($value, TestEnum::getValueByKey($key));
$this->assertSame($key, TestEnum::getKeyByValue($value));
$this->assertSame($value, TestEnum::findValueByKey($key));
$this->assertSame($key, TestEnum::findKeyByValue($value));
$this->assertNull(TestEnum::findValueByKey('__NONEXISTENT_KEY__'));
$this->assertNull(TestEnum::findKeyByValue('__NONEXISTENT_VALUE__'));
$this->assertSame(['LOREM', 'IPSUM', 'DOLOR'], TestEnum::getKeys());
$this->assertSame(['foo', 123, null], TestEnum::getValues());
$this->assertSame(['LOREM' => true, 'IPSUM' => true, 'DOLOR' => true], TestEnum::getKeyMap());
Expand All @@ -54,6 +58,11 @@ function testShouldPerformStaticOperations(string $key, $value)
$this->assertSame(3, TestEnum::count());
}

function testShouldNotBeCloneaeble()
{
$this->assertFalse((new \ReflectionClass(TestEnum::class))->isCloneable());
}

function testShouldThrowExceptionWhenCreatingFromInvalidKey()
{
$this->expectException(InvalidKeyException::class);
Expand Down Expand Up @@ -85,6 +94,28 @@ function testShouldThrowExceptionWhenCallingUnknownFactoryMethod()
TestEnum::_UNKNOWN_KEY_();
}

function testShouldThrowExceptionWhenGettingValueForInvalidKey()
{
$this->expectException(InvalidKeyException::class);
$this->expectExceptionMessage(
'The key "__NONEXISTENT_KEY__" is not defined in enum class "Kuria\Enum\TestSubject\TestEnum"'
. ', known keys: LOREM, IPSUM, DOLOR'
);

TestEnum::getValueByKey('__NONEXISTENT_KEY__');
}

function testShouldThrowExceptionWhenGettingKeyForInvalidValue()
{
$this->expectException(InvalidValueException::class);
$this->expectExceptionMessage(
'The value "__NONEXISTENT_VALUE__" is not defined in enum class "Kuria\Enum\TestSubject\TestEnum"'
. ', known values: "foo", 123, NULL'
);

TestEnum::getKeyByValue('__NONEXISTENT_VALUE__');
}

function testShouldThrowExceptionOnDuplicateValues()
{
$this->expectException(DuplicateValueException::class);
Expand Down Expand Up @@ -157,7 +188,7 @@ function testShouldPerformValueTypeCoercion(string $enumClass, $actualValue, $co
/** @var Enum $enumClass */
$this->assertTrue($enumClass::hasValue($actualValue));
$this->assertTrue($enumClass::hasValue($coercibleValue));
$this->assertSame($actualValue, $enumClass::findValueByKey($enumClass::findKeyByValue($coercibleValue)));
$this->assertSame($actualValue, $enumClass::getValueByKey($enumClass::getKeyByValue($coercibleValue)));
}

/**
Expand All @@ -176,7 +207,7 @@ function testShouldNotCoerceIncompatibleValueTypes(string $enumClass, $actualVal

$this->expectException(InvalidValueException::class);

TestEnum::findKeyByValue($noncoercibleValue);
TestEnum::getKeyByValue($noncoercibleValue);
}

function testShouldEnsureKeyExists()
Expand Down

0 comments on commit e5b674a

Please sign in to comment.