From c2f0e7faf7c3481207ea8270c5cf3a6614730f4a Mon Sep 17 00:00:00 2001 From: Samuel Levy Date: Sat, 12 Mar 2022 22:56:43 +1000 Subject: [PATCH] Extend functionality to work with pure enums --- README.md | 52 ++++++++++++++++++++++++++++--- src/InvokableCases.php | 6 ++-- src/Options.php | 7 ++++- tests/Pest.php | 8 +++++ tests/Pest/InvokableCasesTest.php | 21 +++++++++++-- tests/Pest/NamesTest.php | 6 +++- tests/Pest/OptionsTest.php | 8 ++++- tests/Pest/ValuesTest.php | 6 +++- 8 files changed, 99 insertions(+), 15 deletions(-) diff --git a/README.md b/README.md index 4a54fe0..fa352a0 100644 --- a/README.md +++ b/README.md @@ -21,7 +21,7 @@ composer require archtechx/enums ### InvokableCases -This helper lets you get the value of a backed enum by "invoking" it — either statically (`MyEnum::FOO()` instead of `MyEnum::FOO`), or as an instance (`$enum()`). +This helper lets you get the value of a backed enum, or the name of a pure enum by "invoking" it — either statically (`MyEnum::FOO()` instead of `MyEnum::FOO`), or as an instance (`$enum()`). That way, you can use enums as array keys: ```php @@ -58,6 +58,15 @@ enum TaskStatus: int case COMPLETED = 1; case CANCELED = 2; } + +enum Role +{ + use InvokableCases; + + case ADMINISTRATOR; + case SUBSCRIBER; + case GUEST; +} ``` #### Use static calls to get the primitive value @@ -65,13 +74,16 @@ enum TaskStatus: int TaskStatus::INCOMPLETE(); // 0 TaskStatus::COMPLETED(); // 1 TaskStatus::CANCELED(); // 2 +Role::ADMINISTRATOR(); // 'ADMINISTRATOR' +Role::SUBSCRIBER(); // 'SUBSCRIBER' +Role::GUEST(); // 'GUEST' ``` #### Invoke instances to get the primitive value ```php -public function updateStatus(TaskStatus $status) +public function updateStatus(TaskStatus $status, Role $role) { - $this->record->setStatus($status()); + $this->record->setStatus($status(), $role()); } ``` @@ -91,16 +103,26 @@ enum TaskStatus: int case COMPLETED = 1; case CANCELED = 2; } + +enum Role +{ + use Names; + + case ADMINISTRATOR; + case SUBSCRIBER; + case GUEST; +} ``` #### Use the `names()` method ```php TaskStatus::names(); // ['INCOMPLETE', 'COMPLETED', 'CANCELED'] +Role::names(); // ['ADMINISTRATOR', 'SUBSCRIBER', 'GUEST'] ``` ### Values -This helper returns a list of case *values* in the enum. +This helper returns a list of case *values* in the enum. _**NB:** pure enums don't have values, so this will only ever return an empty array._ #### Apply the trait on your enum ```php @@ -114,16 +136,26 @@ enum TaskStatus: int case COMPLETED = 1; case CANCELED = 2; } + +enum Role +{ + use Values; + + case ADMINISTRATOR; + case SUBSCRIBER; + case GUEST; +} ``` #### Use the `values()` method ```php TaskStatus::values(); // [0, 1, 2] +Role::values(); // [] ``` ### Options -This helper returns an associative array of case names and values. +This helper returns an associative array of case names and values for backed enums, or an array of names for pure enums (making this functionally equivalent to `::names()` for pure Enums). #### Apply the trait on your enum ```php @@ -137,11 +169,21 @@ enum TaskStatus: int case COMPLETED = 1; case CANCELED = 2; } + +enum Role +{ + use Options; + + case ADMINISTRATOR; + case SUBSCRIBER; + case GUEST; +} ``` #### Use the `options()` method ```php TaskStatus::options(); // ['INCOMPLETE' => 0, 'COMPLETED' => 1, 'CANCELED' => 2] +Role::options(); // ['ADMINISTRATOR', 'SUBSCRIBER', 'GUEST'] ``` ## Development diff --git a/src/InvokableCases.php b/src/InvokableCases.php index 54f5426..9a1dbcf 100644 --- a/src/InvokableCases.php +++ b/src/InvokableCases.php @@ -9,17 +9,17 @@ trait InvokableCases /** Return the enum's value when it's $invoked(). */ public function __invoke() { - return $this->value; + return $this->value ?? $this->name; } - /** Return the enum's value when it's called ::STATICALLY(). */ + /** Return the enum's value or name when it's called ::STATICALLY(). */ public static function __callStatic($name, $args) { $cases = static::cases(); foreach ($cases as $case) { if ($case->name === $name) { - return $case->value; + return $case instanceof \BackedEnum ? $case->value : $case->name; } } diff --git a/src/Options.php b/src/Options.php index 2cd620d..e4d2299 100644 --- a/src/Options.php +++ b/src/Options.php @@ -9,6 +9,11 @@ trait Options /** Get an associative array of [case name => case value]. */ public static function options(): array { - return array_column(static::cases(), 'value', 'name'); + $cases = static::cases(); + if (reset($cases) instanceof \BackedEnum) { + return array_column($cases, 'value', 'name'); + } + + return array_column($cases, 'name'); } } diff --git a/tests/Pest.php b/tests/Pest.php index 35b60c5..4e535cc 100644 --- a/tests/Pest.php +++ b/tests/Pest.php @@ -56,3 +56,11 @@ enum Status: int case PENDING = 0; case DONE = 1; } + +enum Role +{ + use InvokableCases, Options, Names, Values; + + case ADMIN; + case GUEST; +} diff --git a/tests/Pest/InvokableCasesTest.php b/tests/Pest/InvokableCasesTest.php index ba23579..6dd5b8a 100644 --- a/tests/Pest/InvokableCasesTest.php +++ b/tests/Pest/InvokableCasesTest.php @@ -2,18 +2,33 @@ use ArchTech\Enums\Exceptions\UndefinedCaseError; -it('can be used as a static method', function () { +it('can be used as a static method with backed enums', function () { expect(Status::PENDING())->toBe(0); expect(Status::DONE())->toBe(1); }); -it('can be invoked as an instance', function () { +it('can be used as a static method with pure enums', function () { + expect(Role::ADMIN())->toBe('ADMIN'); + expect(Role::GUEST())->toBe('GUEST'); +}); + +it('can be invoked as an instance as a backed enum', function () { $status = Status::PENDING; expect($status())->toBe(0); expect($status())->toBe($status->value); }); -it('throws an error when a nonexistent case is being used', function () { +it('can be invoked as an instance as a pure enum', function () { + $role = Role::ADMIN; + + expect($role())->toBe('ADMIN'); +}); + +it('throws an error when a nonexistent case is being used for backed enums', function () { Status::INVALID(); })->expectException(UndefinedCaseError::class); + +it('throws an error when a nonexistent case is being used for pure enums', function () { + Role::INVALID(); +})->expectException(UndefinedCaseError::class); diff --git a/tests/Pest/NamesTest.php b/tests/Pest/NamesTest.php index 1ea3eea..ec39e3f 100644 --- a/tests/Pest/NamesTest.php +++ b/tests/Pest/NamesTest.php @@ -1,5 +1,9 @@ expect(Status::names()) ->toBe(['PENDING', 'DONE']); + +it('can return an array of case names from pure enums') + ->expect(Role::names()) + ->toBe(['ADMIN', 'GUEST']); diff --git a/tests/Pest/OptionsTest.php b/tests/Pest/OptionsTest.php index 1498efd..b581223 100644 --- a/tests/Pest/OptionsTest.php +++ b/tests/Pest/OptionsTest.php @@ -1,7 +1,13 @@ expect(Status::options())->toBe([ 'PENDING' => 0, 'DONE' => 1, ]); + +it('can return an indexed array of options from a pure enum') + ->expect(Role::options())->toBe([ + 0 => 'ADMIN', + 1 => 'GUEST', + ]); diff --git a/tests/Pest/ValuesTest.php b/tests/Pest/ValuesTest.php index 89f8f86..d2b5c58 100644 --- a/tests/Pest/ValuesTest.php +++ b/tests/Pest/ValuesTest.php @@ -1,5 +1,9 @@ expect(Status::values()) ->toBe([0, 1]); + +it('can returns an empty array from a pure enum') + ->expect(Role::values()) + ->toBe([]);